关于这本电子书

About This eBook

ePUB 是一种开放的行业标准电子书格式。然而,ePUB 的支持及其许多功能因阅读设备和应用程序而异。使用您的设备或应用程序设置根据您的喜好自定义演示文稿。您可以自定义的设置通常包括字体、字体大小、单列或双列、横向或纵向模式以及可以单击或点击放大的图形。有关阅读设备或应用程序的设置和功能的更多信息,请访问设备制造商的网站。

ePUB is an open, industry-standard format for eBooks. However, support of ePUB and its many features varies across reading devices and applications. Use your device or app settings to customize the presentation to your liking. Settings that you can customize often include font, font size, single or double column, landscape or portrait mode, and figures that you can click or tap to enlarge. For additional information about the settings and features on your reading device or app, visit the device manufacturer’s Web site.

许多标题都包含编程代码或配置示例。要优化这些元素的呈现,请以单栏、横向模式查看电子书,并将字体大小调整为最小设置。除了以可重排文本格式呈现代码和配置之外,我们还提供了模仿印刷书中演示的代码图像;因此,在可回流格式可能会影响代码列表的呈现的情况下,您将看到“单击此处查看代码图像”链接。单击链接可查看打印保真度代码图像。要返回到上一个查看的页面,请单击设备或应用程序上的“后退”按钮。

Many titles include programming code or configuration examples. To optimize the presentation of these elements, view the eBook in single-column, landscape mode and adjust the font size to the smallest setting. In addition to presenting code and configurations in the reflowable text format, we have included images of the code that mimic the presentation found in the print book; therefore, where the reflowable format may compromise the presentation of the code listing, you will see a “Click here to view code image” link. Click the link to view the print-fidelity code image. To return to the previous page viewed, click the Back button on your device or app.

活文档

Living Documentation

活文档

Living Documentation

通过设计持续知识共享

Continuous Knowledge Sharing by Design

西里尔·马特莱尔

Cyrille Martraire

艾迪生韦斯利的标志。

波士顿•哥伦布•纽约•旧金山•阿姆斯特丹•开普敦•

迪拜•伦敦•马德里•米兰•慕尼黑•巴黎•蒙特利尔•多伦多•德里

•墨西哥城•圣保罗•悉尼•香港•首尔•新加坡•台北•东京

Boston • Columbus • New York • San Francisco • Amsterdam • Cape Town

Dubai • London • Madrid • Milan • Munich • Paris • Montreal • Toronto • Delhi

Mexico City • São Paulo • Sydney • Hong Kong • Seoul • Singapore • Taipei • Tokyo

制造商和销售商用来区分其产品的许多名称都被称为商标。如果这些名称出现在本书中,并且出版商知道商标声明,则这些名称均以首字母大写或全部大写印刷。

Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and the publisher was aware of a trademark claim, the designations have been printed with initial capital letters or in all capitals.

作者和出版商在本书的准备过程中非常谨慎,但没有做出任何形式的明示或暗示的保证,并且对错误或遗漏不承担任何责任。对于因使用此处包含的信息或程序而产生的偶然或间接损害,我们不承担任何责任。

The author and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein.

有关批量购买本书的信息,或特殊销售机会(可能包括电子版本;定制封面设计;以及针对您的业务、培训目标、营销重点或品牌利益的特定内容),请联系我们的公司销售部门请发送电子邮件至corpsales@pearsoned.com或 (800) 382-3419。

For information about buying this title in bulk quantities, or for special sales opportunities (which may include electronic versions; custom cover designs; and content particular to your business, training goals, marketing focus, or branding interests), please contact our corporate sales department at corpsales@pearsoned.com or (800) 382-3419.

对于政府销售查询,请联系governmentsales@pearsoned.com

For government sales inquiries, please contact governmentsales@pearsoned.com.

有关美国境外销售的问题,请联系intlcs@pearson.com

For questions about sales outside the U.S., please contact intlcs@pearson.com.

请访问我们的网站:informit.com/aw

Visit us on the Web: informit.com/aw

美国国会图书馆控制号:2019936806

Library of Congress Control Number: 2019936806

版权所有 © 2019 培生教育公司

Copyright © 2019 Pearson Education, Inc.

版权所有。本出版物受版权保护,在进行任何禁止复制、存储在检索系统中或以任何形式或方式(电子、机械、影印、记录或类似方式)传输之前,必须获得出版商的许可。有关权限、申请表以及培生教育全球权利与权限部门内相应联系人的信息,请访问www.pearsoned.com/permissions/

All rights reserved. This publication is protected by copyright, and permission must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying, recording, or likewise. For information regarding permissions, request forms and the appropriate contacts within the Pearson Education Global Rights & Permissions Department, please visit www.pearsoned.com/permissions/.

ISBN-13: 978-0-13-468932-6

ISBN-10: 0-13-468932-1

ISBN-13: 978-0-13-468932-6

ISBN-10: 0-13-468932-1

1 19

1   19

出版商

Publisher

马克·陶布

Mark L. Taub

开发编辑

Development Editor

克里斯·赞恩

Chris Zahn

总编辑

Managing Editor

桑德拉·施罗德

Sandra Schroeder

高级项目编辑

Senior Project Editor

托尼娅·辛普森

Tonya Simpson

文案编辑器

Copy Editor

基蒂·威尔逊

Kitty Wilson

索引器

Indexer

埃里卡·米伦

Erika Millen

校对

Proofreader

琳达·莫里斯

Linda Morris

技术评审员

Technical Reviewers

丽贝卡·维尔夫斯-布洛克

Rebecca Wirfs-Brock

伍迪·祖尔

Woody Zuill

史蒂夫·海耶斯

Steve Hayes

编辑助理

Editorial Assistant

辛迪·提特斯

Cindy Teeters

封面设计师

Cover Designer

楚蒂·普拉塞西斯

Chuti Prasertsith

合成器

Compositor

代码咒语

codeMantra

图形

Graphics

夏云山

Yunshan Xia

献给我的妻子云珊,以及我们的孩子诺伯特和古斯塔夫。

For my wife, Yunshan, and our children, Norbert and Gustave.

内容

Contents

致谢

Acknowledgments

关于作者

About the Author

介绍

Introduction

第 1 章:重新思考文档

Chapter 1: Rethinking Documentation

活生生的土地上的故事

A Tale from the Land of Living Documentation

为什么有这个功能?

Why This Feature?

明天你将不再需要这张草图

Tomorrow You Won’t Need This Sketch Anymore

抱歉,我们没有营销文件!

Sorry, We Don’t Have Marketing Documents!

你一直用这个词,但这不是它的意思

You Keep Using This Word, but This Is Not What It Means

给我看大局,你就会发现哪里出了问题

Show Me the Big Picture, and You’ll See What’s Wrong There

活文档的未来就在眼前

The Future of Living Documentation Is Now

传统文档的问题

The Problem with Traditional Documentation

通常,文档并不酷

Documentation Is Not Cool, Usually

文档的缺陷

The Flaws of Documentation

敏捷宣言和文档

The Agile Manifesto and Documentation

现在是文档 2.0 的时候了

It’s Time for Documentation 2.0

文档是关于知识的

Documentation Is About Knowledge

知识的起源

The Origination of Knowledge

知识如何演化?

How Does Knowledge Evolve?

为什么需要知识

Why Knowledge Is Necessary

文档是关于知识的传递

Documentation Is About Transferring Knowledge

专注于重要的事情

Focusing on What Matters

活文档的核心原则

Core Principles of Living Documentation

可靠的

Reliable

省力

Low Effort

协作性

Collaborative

富有洞察力

Insightful

蚂蚁如何交换知识:Stigmergy

How Ants Exchange Knowledge: Stigmergy

大多数知识已经存在

Most Knowledge Is Already There

内部文档

Internal Documentation

内部文档与外部文档

Internal Versus External Documentation

内部和外部文档示例

Examples of Internal and External Documentation

优先选择内部文档

Preferring Internal Documentation

现场记录

In Situ Documentation

机器可读文档

Machine-Readable Documentation

特定知识与通用知识

Specific Versus Generic Knowledge

学习通用知识

Learning Generic Knowledge

专注于特定知识

Focusing on Specific Knowledge

确保文档准确性

Ensuring Documentation Accuracy

可靠文档的准确性机制

Accuracy Mechanism for Reliable Documentation

当文档不需要准确性机制时

When Documentation Does Not Need an Accuracy Mechanism

挑战你的文档的大问题

Big Questions to Challenge Your Documentation

质疑是否需要文档

Questioning the Need for Documentation at All

由于缺乏信任而需要文档

Need for Documentation Because of Lack of Trust

即时文档,或未来知识的廉价选择

Just-in-Time Documentation, or a Cheap Option on Future Knowledge

质疑传统文档的必要性

Questioning the Need for Traditional Documentation

立即减少额外工作

Minimizing Extra Work Now

最大限度地减少以后的额外工作

Minimizing Extra Work Later

让活动变得有趣

Making an Activity Fun

文档重启

Documentation Reboot

活文档:非常简短的版本

Living Documentation: The Very Short Version

更好的文档编制方法

Approaches to Better Documentation

通往 DDD 的门户

A Gateway to DDD

简而言之,领域驱动设计

Domain-Driven Design in a Nutshell

实时文档和领域驱动设计

Living Documentation and Domain-Driven Design

当活文档成为 DDD 的应用时

When Living Documentation Is an Application of DDD

BDD、DDD、XP 和活文档之间相互根源的故事

A Story of Mutual Roots Between BDD, DDD, XP, and Living Documentation

概括

Summary

第 2 章:行为驱动开发作为生活规范的示例

Chapter 2: Behavior-Driven Development as an Example of Living Specifications

BDD 就是对话

BDD Is All About Conversations

自动化 BDD 就是活文档

BDD with Automation Is All About Living Documentation

冗余与协调

Redundancy and Reconciliation

文件中场景的剖析

The Anatomy of Scenarios in a File

功能文件的意图

The Intent of a Feature File

特征文件场景

Feature File Scenarios

规格详情

Specification Details

特征文件中的标签

Tags in Feature Files

场景作为互动活文档

Scenarios as Interactive Living Documentation

无聊的纸质文档中的场景

Scenarios in Boring Paper Documents

特征文件示例

A Feature File Example

各个方面的活文档的典型案例

A Canonical Case of Living Documentation in Every Aspect

更进一步:充分利用您的活文档

Going Further: Getting the Best of Your Living Documentation

基于属性的测试和 BDD

Property-Based Testing and BDD

概括

Summary

第 3 章:知识利用

Chapter 3: Knowledge Exploitation

识别权威知识

Identifying Authoritative Knowledge

知识现在在哪里?

Where Is the Knowledge Now?

单一来源出版

Single-Source Publishing

生成已发布文档的一些示例

Some Examples of Producing a Published Document

带有版本号的已发布快照

A Published Snapshot with a Version Number

评论

Remarks

建立对账机制(又名验证机制)

Setting Up a Reconciliation Mechanism (aka Verification Mechanism)

运行一致性测试

Running Consistency Tests

测试假设的调节

Reconciliation on the Test Assumptions

已发布的合同

Published Contracts

整合分散的事实

Consolidating Dispersed Facts

整合如何运作

How Consolidation Works

合并实施注意事项

Consolidation Implementation Considerations

现成的文档

Ready-Made Documentation

标准词汇的力量

The Power of a Standard Vocabulary

链接到标准知识

Linking to Standard Knowledge

不仅仅是词汇

More Than Just Vocabulary

在对话中使用现成的知识来加速知识转移

Using Ready-Made Knowledge in Conversation to Speed Up Knowledge Transfer

工具历史

Tools History

概括

Summary

第 4 章:知识增强

Chapter 4: Knowledge Augmentation

当编程语言不够用时

When Programming Languages Are Not Enough

使用注释的文档

Documentation Using Annotations

注释不仅仅是标签

Annotations as More Than Tags

描述决策背后的理由

Describing the Rationale Behind Decisions

嵌入式学习

Embedded Learning

按惯例记录文件

Documentation by Convention

带有约定的遗留代码中的活文档

Living Documentation in Legacy Code with Conventions

记录约定

Documenting the Conventions

一贯遵守惯例

Consistently Adhering to Conventions

约定的局限性

The Limitations of Conventions

外部文档方法

External Documentation Methods

边车文件

Sidecar Files

元数据数据库

Metadata Databases

设计自定义注释

Designing Custom Annotations

刻板属性

Stereotypical Properties

刻板印象和战术模式

Stereotypes and Tactical Patterns

使用有意义的注释包名称

Using Meaningful Annotation Package Names

劫持标准注释

Hijacking Standard Annotations

标准注解:@Aspect 和面向方面的编程

Standard Annotation: @Aspect and Aspect-Oriented Programming

默认注释或除非必要

Annotation by Default or Unless Necessary

处理模块范围的知识

Handling Module-Wide Knowledge

处理多种模块

Dealing with Many Kinds of Modules

实践中的模块范围增强

Module-Wide Augmentation In Practice

内在知识增强

Intrinsic Knowledge Augmentation

机器可访问的文档

Machine-Accessible Documentation

记录你的理由

Recording Your Rationale

基本原理是什么?

What’s in a Rationale?

明确理由

Making the Rationale Explicit

超越文档:动机设计

Beyond Documentation: Motivated Design

避免记录猜测

Avoid Documenting Speculation

技能作为预先记录的理由

Skills as Pre-Documented Rationales

记录作为变革推动者的基本原理

Recording the Rationale as an Enabler for Change

承认你的影响(又名项目书目)

Acknowledging Your Influences (aka Project Bibliography)

宣告你的风格

Declaring Your Style

将消息作为综合文档提交

Commit Messages as Comprehensive Documentation

提交指南

Commit Guidelines

概括

Summary

第五章:生活策展:识别权威知识

Chapter 5: Living Curation: Identifying Authoritative Knowledge

动态策展

Dynamic Curation

动态策展的例子

Examples of Dynamic Curation

编辑策划

Editorial Curation

低维护动态管理

Low-Maintenance Dynamic Curation

一套知识集多种用途

One Corpus of Knowledge for Multiple Uses

情景摘要

Scenario Digests

突出核心

Highlighting the Core

突出鼓舞人心的榜样

Highlighting Inspiring Exemplars

导游和观光地图

Guided Tours and Sightseeing Maps

创建观光地图

Creating a Sightseeing Map

创建导游

Creating a Guided Tour

创建生动的导游

Creating a Living Guided Tour

穷人的识字编程

A Poor Man’s Literate Programming

总结:策展人准备艺术展览

Summing Up: The Curator Preparing an Art Exhibition

选择和组织现有知识

Selecting and Organizing Existing Knowledge

需要时添加缺少的内容

Adding What’s Missing When Needed

为无法参加的人和后代提供便利

Accessibility for People Who Can’t Attend and for Posterity

概括

Summary

第 6 章:自动化文档编制

Chapter 6: Automating Documentation

活文件

Living Documents

创建活文档的步骤

Steps in Creating a Living Document

演示规则

Presentation Rules

生活词汇

Living Glossaries

生活词汇表如何运作

How a Living Glossary Works

请举个例子!

An Example Please!

活文档的信息管理

Information Curation for Living Documents

在有界上下文中创建术语表

Creating a Glossary Within a Bounded Context

生活词汇表的案例研究

Case Study of a Living Glossary

生活图表

Living Diagrams

图表有助于对话

Diagrams Assist in Conversations

一张图,一个故事

One Diagram, One Story

生动的图表让你保持诚实

Living Diagrams to Keep You Honest

追求完美图表

The Quest for the Perfect Diagram

渲染生动的图表

Rendering a Living Diagram

可视化指南

Visualization Guidelines

示例:六边形架构生活图

Example: Hexagonal Architecture Living Diagram

案例研究:活生生的业务概览

Case Study: A Business Overview as a Living Diagram

示例:上下文图

Example: A Context Diagram

自动生成设计文档的挑战

The Challenges with Automated Generation of Design Documentation

概括

Summary

第 7 章:运行时文档

Chapter 7: Runtime Documentation

示例:生活服务图

Example: Living Services Diagram

增强代码的问题,但在运行时

A Matter of Augmented Code but at Runtime

发现架构

Discovering the Architecture

使这一切发挥作用的魔力

The Magic That Makes This Work

更进一步

Going Further

可见的工作:工作软件作为自己的文档

Visible Workings: Working Software as Its Own Documentation

可见的测试

Visible Tests

特定领域的表示法

Domain-Specific Notation

生成自定义的特定领域图表以获得视觉反馈

Generating Custom Domain-Specific Diagrams to Get Visual Feedback

示例:使用事件溯源时的可见测试

Example: A Visible Test When Using Event Sourcing

代码中的具体示例

A Concrete Example in Code

事件溯源场景的生动图表

Living Diagrams from Event Sourcing Scenarios

自省的工作原理:内存中的代码作为知识的来源

Introspectable Workings: Code in Memory as a Source of Knowledge

反思与反思

Introspecting with Reflection

不反思地内省

Introspecting Without Reflection

概括

Summary

第 8 章:可重构文档

Chapter 8: Refactorable Documentation

代码即文档

Code as Documentation

文字布局

Text Layout

编码约定

Coding Conventions

命名为主要文档

Naming as the Primary Documentation

组合方法:您需要为它们命名

Composed Methods: You Need to Name Them

惯用命名是上下文相关的

Idiomatic Naming Is Contextual

针对框架进行编码

Coding Against a Framework

类型驱动文档

Type-Driven Documentation

从基元到类型

From Primitives to Types

记录类型和集成文档

Documented Types and Integrated Documentation

类型和关联

Types and Associations

类型胜过评论

Types over Comments

组合方法

The Composed Method

流畅的风格

Fluent Style

使用内部 DSL

Using an Internal DSL

实现流畅的界面

Implementing a Fluent Interface

流利测试

Fluent Tests

创建 DSTL

Creating a DSTL

何时不使用 Fluent 样式

When Not to Use a Fluent Style

案例研究:以注释为指导的重构代码示例

Case Study: An Example of Refactoring Code, Guided by Comments

综合文档

Integrated Documentation

类型层次结构

Type Hierarchy

代码搜索

Code Searching

源自实际使用的语义

Semantics Derived from Actual Usage

使用纯文本图表

Using Plain-Text Diagrams

示例:纯文本图

Example: Plain-Text Diagrams

图表即代码

Diagrams as Code

概括

Summary

第 9 章:稳定的文档

Chapter 9: Stable Documentation

常青内容

Evergreen Content

需求比设计决策更稳定

Requirements Are More Stable Than Design Decisions

高水平目标往往是稳定的

High-Level Goals Tend to Be Stable

很多知识并不像看起来那么稳定

A Lot of Knowledge Is Less Stable Than It Looks

案例研究:自述文件

Case Study: A README File

常绿文档的技巧

Tips for Evergreen Documentation

避免将策略文档与其实施文档混合

Avoiding Mixing Strategy Documentation with the Documentation of Its Implementation

确保稳定

Ensuring Stability

使用常年命名

Using Perennial Naming

沿着稳定的轴组织工件

Organizing Artifacts Along Stable Axes

关联知识

Linked Knowledge

不稳定到稳定的依赖关系

Volatile-to-Stable Dependencies

断开的链接检查器

Broken Link Checkers

链接注册表

Link Registry

添加书签的搜索

Bookmarked Searches

稳定知识的类别

Categories of Stable Knowledge

长青自述文件

Evergreen README

愿景声明

Vision Statement

领域愿景声明

Domain Vision Statements

目标

Goals

影响图

Impact Mapping

投资稳定的知识

Investing in Stable Knowledge

领域沉浸

Domain Immersion

调查墙

Investigation Wall

领域培训

Domain Training

活出我的生活课程

Live-My-Life Sessions

影子用户

Shadow Users

长期投资

A Long-Term Investment

概括

Summary

第 10 章:避免传统文档

Chapter 10: Avoiding Traditional Documentation

关于正式文档的对话

Conversations About Formal Documentation

Wiio的法则

Wiio’s Laws

三种解释规则

The Rule of Three Interpretations

谈话的障碍

Obstacles to Conversations

共同努力,持续共享知识

Working Collectively for Continuous Knowledge Sharing

结对编程

Pair Programming

交叉编程

Cross Programming

群体编程

Mob Programming

三个朋友(或更多)

The Three Amigos (or More)

事件风暴作为入职流程

Event Storming as an Onboarding Process

知识转移会议

Knowledge Transfer Sessions

持续记录

Continuous Documentation

卡车因素

Truck Factor

咖啡机通讯

Coffee Machine Communication

理念沉淀

Idea Sedimentation

废弃文档

Throw-Away Documentation

按需文档

On-Demand Documentation

即时文档

Just-in-Time Documentation

尽早激发即时学习

Provoking Just-in-Time Learning Early

惊讶报告

Astonishment Report

包括一些前期文档

Including Some Upfront Documentation

交互式文档

Interactive Documentation

声明式自动化

Declarative Automation

陈述式风格

Declarative Style

声明式依赖管理

Declarative Dependency Management

声明式配置管理

Declarative Configuration Management

声明式自动化部署

Declarative Automated Deployment

机器文档

Machine Documentation

关于自动化的一般评论

Remarks on Automation in General

强制准则

Enforced Guidelines

一些规则示例

Some Examples of Rules

改进指南

Evolving the Guidelines

强制执行或鼓励

Enforcement or Encouragement

声明性指南

Declarative Guidelines

工具问题

A Matter of Tools

指南或设计文档?

Guidelines or Design Documentation?

保修贴纸如果被篡改则无效

Warranty Sticker Void if Tampered With

信任第一的文化

Trust-First Culture

行为受限

Constrained Behavior

轻松做正确的事

Making It Easy to Do the Right Thing

让错误成为不可能:防错 API

Making Mistakes Impossible: Error-Proof API

避免文档的设计原则

Design Principles for Documentation Avoidance

可替换性第一

Replaceability First

一致性第一

Consistency First

示例:零文档游戏

Example: The Zero Documentation Game

持续培训

Continuous Training

概括

Summary

第 11 章:超越文档:生动的设计

Chapter 11: Beyond Documentation: Living Design

聆听文档

Listening to the Documentation

领域语言发生了什么?

What Happened to the Language of the Domain?

通过巧合设计进行编程

Programming by Coincidence Design

深思熟虑的决策

Deliberate Decision Making

“深思熟虑的决定”并不意味着“预先决定”

“Deliberate Decision” Does Not Mean “Upfront Decision”

文档是代码审查的一种形式

Documentation Is a Form of Code Review

可耻的文档

Shameful Documentation

示例:可耻的文档

Example: Shameful Documentation

故障排除指南

The Troubleshooting Guide

可耻的代码文档

Shameful Code Documentation

记录错误还是避免错误?

Documenting Errors or Avoiding Errors?

文档驱动的开发

Documentation-Driven Development

让您保持诚实的文档

Documentation to Keep You Honest

文档驱动与“避免文档”之间的明显矛盾

The Apparent Contradiction Between Documentation Driven and “Avoiding Documentation”

滥用活文档(反模式)

Abusing Living Documentation (Anti-pattern)

活文档导致的拖延

Procrastination by Living Documentation

可生物降解的文件

Biodegradable Documentation

卫生透明

Hygienic Transparency

诊断工具

Diagnostic Tools

正压清洁内部

Positive Pressure to Clean the Inside

设计技巧无处不在

Design Skills Everywhere

记者波特采访Living Doc Doc先生

Reporter Porter Interviewing Mr. Living Doc Doc

概括

Summary

第 12 章:生活架构文档

Chapter 12: Living Architecture Documentation

记录问题

Documenting the Problem

问题简介的示例

An Example of a Problem Brief

显式质量属性

Explicit Quality Attributes

权益驱动架构文档

Stake-Driven Architecture Documentation

明确的假设

Explicit Assumptions

简洁体现品质

Brevity Suggests Quality

不断发展:变化友好的文档

Evolving Continuously: Change-Friendly Documentation

决策日志

Decision Logs

结构化决策日志的示例

An Example of a Structured Decision Log

期刊或博客是大脑垃圾场

Journals or Blogs as Brain Dumps

分形架构文档

Fractal Architecture Documentation

建筑景观

The Architecture Landscape

架构图和符号

Architecture Diagrams and Notations

建筑法典

An Architecture Codex

透明架构

Transparent Architecture

架构注释

Architectural Annotations

强制设计决策

Enforced Design Decisions

建筑现实检查

Architectural Reality Check

测试驱动架构

Test-Driven Architecture

质量属性作为场景

Quality Attributes as Scenarios

生产运行时的质量属性

Quality Attributes at Runtime in Production

其他质量属性

Other Quality Attributes

从零散的知识到可用的文档

From Fragmented Knowledge to Usable Documentation

作为活建筑文档的小规模模拟

Small-Scale Simulation as Living Architecture Documentation

小规模模拟的理想特性

The Desirable Properties of a Small-Scale Simulation

简化系统的技术

Techniques to Simplify a System

构建小规模模拟已经是乐趣的一半

Building a Small-Scale Simulation Is Half the Fun

系统隐喻

System Metaphor

通过谈论另一个系统来解释一个系统

Explaining a System by Talking About Another System

即使没有先验知识也很有用

Useful Even Without Prior Knowledge

另一个隐喻中的隐喻

A Metaphor in Another Metaphor

概括

Summary

第 13 章:将活文档引入新环境

Chapter 13: Introducing Living Documentation to a New Environment

卧底实验

Undercover Experiments

官方的野心

Official Ambition

新事物必须有效并且必须被接受

New Things Have to Work and Have to Be Accepted

轻轻开始

Starting Gently

变得更大、更可见

Going Big and Visible

案例研究:向团队成员介绍活文档的故事

Case Study: A Tale of Introducing Living Documentation to a Team Member

对话第一

Conversations First

第一次汇报

The First Debriefing

是时候谈谈代码了

Time to Talk About the Code

决策日志和导游

Decision Logs and Guided Tours

对活文档的常见反对意见

Common Objections to Living Documentation

注释并不用于文档

Annotations Are Not Meant for Documentation

“我们已经这么做了”

“We Do It Already”

将旧文档迁移到实时文档中

Migrating Legacy Documentation into Living Documentation

边际文件

Marginal Documentation

案例研究:在批处理系统中引入实时文档

Case Study: Introducing Living Documentation in a Batch System

自述文件和现成文档

README and Ready-Made Documentation

商业行为

Business Behavior

可见的运作和单一的事实来源

Visible Workings and a Single Source of Truth

为开发人员提供的综合文档和为其他利益相关者提供的实时术语表

Integrated Documentation for Developers and a Living Glossary for Other Stakeholders

生动的图表展示设计意图

A Living Diagram to Show the Design Intent

联系信息和导游

Contact Information and Guided Tours

微服务大局

Microservices Big Picture

向管理层出售活文件

Selling Living Documentation to Management

从实际问题开始

Starting with an Actual Problem

活生生的文献记录倡议

A Living Documentation Initiative

将现状与美好世界的承诺进行对比,以满足人们的愿望

Contrasting the Current Situation with the Promise of a Better World to Match People’s Aspirations

精神上的顺从

Compliance in Spirit

案例研究:遵守 ITIL

Case Study: Compliance with ITIL

ITIL 示例

The ITIL Example

概括

Summary

第 14 章:记录遗留应用程序

Chapter 14: Documenting Legacy Applications

文件破产

Documentation Bankruptcy

遗留应用程序成为僵化的知识

Legacy Application as Fossilized Knowledge

考古学

Archeology

气泡上下文

Bubble Context

叠加结构

Superimposed Structure

突出显示的结构

Highlighted Structure

外部注释

External Annotations

可生物降解转化

Biodegradable Transformation

示例:Strangler 应用程序

Example: Strangler Application

示例:破产

Example: Bankruptcy

同意格言

Agree on Maxims

强制执行的旧规则

Enforced Legacy Rules

概括

Summary

第 15 章:额外:显眼的文档

Chapter 15: Extra: Conspicuous Documentation

关注差异

Focusing on Differences

你的柠檬怎么样?

How Is Your Lemon?

只告诉未知的事情

Tell Only What’s Unknown

按已知受众细分

Segmenting by Known Audience

灵活的内容

Flexible Content

低保真内容

Low-Fidelity Content

视觉引导

Visual Facilitation

便于搜索的文档

Search-Friendly Documentation

具体例子,现在一起

Concrete Examples, Together, Now

在实践中

In Practice

快速媒体和事先准备

Fast Media and Prior Preparation

在一起,现在

Together, Now

堆栈溢出文档

Stack Overflow Documentation

价格实惠且有吸引力

Affordable and Attractive

规格摘要

Specs Digest

复活节彩蛋和有趣的轶事

Easter Eggs and Fun Anecdotes

宣传新闻

Promoting News

非正统媒体

Unorthodox Media

格言

Maxims

海报和国内广告

Posters and Domestic Ads

基于模因的海报

Meme-Based Posters

信息辐射器

Information Radiators

幽默和廉价媒体

Humor and Cheap Media

好东西/赃物

Goodies/Swag

漫画

Comics

信息甲板

Infodecks

可视化和动画

Visualizations and Animations

乐高积木

LEGO Bricks

家具

Furniture

3D 打印的东西

3D Printed Stuff

概括

Summary

指数

Index

致谢

Acknowledgments

首先,我要特别感谢我的官方审稿人 Rebecca Wirfs-Brock、Steve Hayes 和 Woody Zuill,他们在很短的时间内对手稿进行了富有洞察力的审阅,这确实有助于更好地改进和组织材料。

First, I’d like to give special thanks my official reviewers, Rebecca Wirfs-Brock, Steve Hayes, and Woody Zuill, for the insightful review of the manuscript in a very short period of time, which really helped improve and organize the material better.

非常感谢 Pearson 团队,首先是 Chris Zahn,我有幸经常与之合作的开发编辑,Mark Taub,领导整个出版过程的人,Kitty Wilson 负责细致的文案编辑,还有 Tonya Simpson,它负责整个出版过程。很高兴在整个项目中与您合作。我还要感谢执行主编 Chris Guzikowski 于 2016 年在培生集团签署了这本书。

Many thanks to the Pearson team, starting with Chris Zahn, the developmental editor I’ve been lucky to work with regularly, Mark Taub, publisher, who led the whole publishing process, Kitty Wilson for the meticulous copy editing, and Tonya Simpson, it was a pleasure working with you throughout the project. I also want to thank Chris Guzikowski, executive editor, for signing the book at Pearson back in 2016.

本书中的想法源自我非常尊敬的人。Dan North、Chris Matts 和 Liz Keogh 衍生出了一种称为行为驱动开发 (BDD) 的实践,这是工作中活文档的最佳示例之一。Eric Evans 在他的《领域驱动设计》一书中提出了许多想法,这些想法反过来又激发了 BDD。Gojko Adzic在他的《Specification by Examples》一书中提出了“活文档”这个术语。在本书中,我详细阐述了这些想法,并将它们推广到软件项目的其他领域。DDD 强调了思维在项目生命周期中如何演变,其支持者提出了统一领域模型和代码。同样,本书建议统一项目工件和文档。

The ideas in this book originate from people I respect a lot. Dan North, Chris Matts, and Liz Keogh derived the practice called behavioral driven development (BDD), which is one of the best examples of living documentation at work. Eric Evans, in his book Domain-Driven Design, proposed many ideas that, in turn, inspired BDD. Gojko Adzic proposed the term living documentation in his book Specification by Example. In this book, I elaborate on these ideas and generalizes them to other areas of a software project. DDD has emphasized how the thinking evolves during the life of a project, and its proponents have proposed unifying the domain model and code. Similarly, this book suggests unifying project artifacts and documentation.

以 Ward Cunningham 和 Kent Beck 为首的模式运动及其作者已经越来越明显地表明,可以通过参考已经在程序模式语言 (PLoP) 会议上发布或提出的模式来更好地进行文档记录。

The patterns movement and its authors, starting with Ward Cunningham and Kent Beck, have made it increasingly obvious that it is possible to do better documentation by referring to patterns, those already published or presented at Pattern Languages of Programs (PLoP) conferences.

务实程序员 Martin Fowler、Ade Oshyneye、Andreas Rüping、Simon Brown 和许多其他作者就如何以更好的方式编写更好的文档提炼出了宝贵的智慧。里纳特·阿卜杜林 (Rinat Abdulin) 首先在活生生的图表上进行写作,并且确实创造了这个术语。谢谢你们大家!

Pragmatic Programmers, Martin Fowler, Ade Oshyneye, Andreas Rüping, Simon Brown, and many other authors have distilled nuggets of wisdom on how to do better documentation, in a better way. Rinat Abdulin first wrote on living diagrams and, indeed, coined the term. Thanks to you all of you!

埃里克·埃文斯(Eric Evans),感谢您的所有讨论(通常不是关于本书的讨论)以及您的建议。

Eric Evans, thanks for all the discussions, usually not on this book, and for your advice.

我还要感谢 Brian Marick 与我分享他自己在可见工作方面的工作。鼓励很重要,与沃恩·弗农和桑德罗·曼库索讨论写一本书确实对我有帮助,所以谢谢大家!

I would also like to thank Brian Marick for sharing his own work on visible workings with me. As encouragement matters, discussions with Vaughn Vernon and Sandro Mancuso on writing a book did help me, so thanks, guys!

有些讨论比其他讨论更重要;尤其重要的是那些能够产生新想法、带来更好理解或只是令人兴奋的想法。感谢 George Dinwiddie、Paul Rayner、Jeremie Chassaing、Arnauld Loyer 和 Romeu Moura 进行了所有激动人心的讨论并分享了您自己的故事和实验。

Some discussions are more important than others; especially important are those that generate new ideas, lead to better understanding, or are just exciting. Thanks to George Dinwiddie, Paul Rayner, Jeremie Chassaing, Arnauld Loyer, and Romeu Moura for all the exciting discussions and for sharing your own stories and experiments.

在本书的写作过程中,我尽可能多地寻求想法和反馈,特别是在软件开发会议的开放式会议期间。Maxime Sanglan 和 Franziska Sauerwein 给了我第一个令人鼓舞的反馈。谢谢弗兰齐和马克斯!我要感谢我在这些会议和非会议上举办的有关实时文档的会议的所有参与者,例如敏捷法国、苏格拉底德国、苏格拉底法国、Codefreeze 芬兰、Meetup Software Craftsmanship 巴黎圆桌会议和一些 Jams of Code晚上在阿罗拉。

Through the writing of this book, I looked for ideas and feedback as much as I could, particularly during open-space sessions at software development conferences. Maxime Sanglan gave me the first encouraging feedback, along with Franziska Sauerwein. Thanks, Franzi and Max! I want to thank all the participants of the sessions I have run on living documentation at these conferences and unconferences, such as in Agile France, Socrates Germany, Socrates France, Codefreeze Finland, and the Meetup Software Craftsmanship Paris round tables and several Jams of Code at Arolla in the evening.

我已经在会议上发表演讲有一段时间了,但总是涉及我们行业中已经广泛接受的实践。对于活文档等更多新颖的内容,我还必须测试不同受众的接受程度,我感谢第一批冒险选择主题的会议:巴黎的 NCrafts、伦敦的领域驱动设计 eXchange、波尔多的 Bdx.io ,和伊竹布加勒斯特。感谢您主持第一版演讲或研讨会。获得很好的反馈对激发更多努力创作这本书非常有帮助。

I had been giving talks at conferences for some time but always concerning practices that are already widely accepted in our industry. With more novel content like living documentation, I also had to test acceptance from various audiences, and I thank the first conferences that took the risk of select the topic: NCrafts in Paris, Domain-Driven Design eXchange in London, Bdx.io in Bordeaux, and ITAKE Bucharest. Thanks for hosting the first versions of the talk or workshop. It was very helpful to have great feedback to inspire more effort to create the book.

我很幸运在阿罗拉拥有一群充满热情的同事;感谢大家的贡献以及成为我的第一批听众,特别是 Fabien Maury、Romeu Moura、Arnauld Loyer、Yvan Vu 和 Somkiane Vongnoukoun。Somkiane 建议添加故事以使文本“不那么无聊”,这是改进这本书的最佳想法之一。感谢 SGCIB 工艺中心的教练们提供的所有午餐讨论和想法,以及他们对更好地开发软件的热情。我特别要感谢吉尔斯·菲利帕特(Gilles Philippart),本书多次提到他的想法,以及布鲁诺·布卡德(Bruno Boucard)和托马斯·皮尔兰(Thomas Pierrain)。

I am very lucky at Arolla to have a community of passionate colleagues; thank you all for your contributions and for being my very first audience, in particular Fabien Maury, Romeu Moura, Arnauld Loyer, Yvan Vu, and Somkiane Vongnoukoun. Somkiane suggested adding stories to make the text “less boring,” and it was one of the best ideas to improve the book. Thanks to the coaches of the Craftsmanship center at SGCIB for all the lunch discussions and ideas and their enthusiasm to get better at how we do software. In particular, I want to thank Gilles Philippart, who is mentioned several times in this book for his ideas, and Bruno Boucard and Thomas Pierrain.

我还必须感谢 Clémo Charnay 和 Alexandre Pavillon 为 SGCIB 商品交易部门信息系统实验中的一些想法提供了早期支持,并感谢 Bruno Dupuis 和 James Kouthon 帮助使其成为现实。本书中的许多想法已经在我之前合作过的公司中尝试过:SGCIB 的商品部门、Sungard Asset Management 的 Asset Arena 团队、Swapstream 的所有人员、CME 的同事等等。

I must also acknowledge Clémo Charnay and Alexandre Pavillon for providing early support for some of the ideas as experiments in the SGCIB commodity trading department information system and Bruno Dupuis and James Kouthon for their help making it a reality. Many of the ideas in this book have been tried in previous companies I’ve worked with: the Commodity department at SGCIB, the Asset Arena teams at Sungard Asset Management, all the folks at Swapstream, our colleagues at CME, and others.

感谢 Café Loustic 和那里所有出色的咖啡师。这是作为作家工作的完美场所,我在那里写了很多章节,通常由来自 Caffenation 的埃塞俄比亚单一来源咖啡提供动力。谢谢爸爸和妈妈,鼓励我们的自由精神。最后,我要感谢我的妻子云珊,她在本书的写作过程中一直给予我支持和鼓励。非常重要的是,由于您可爱的图片,您还使这本书成为一种更愉快的体验!Chérie,您的支持至关重要,我希望像您支持这本书一样支持您自己的项目。

Thanks to Café Loustic and all the great baristas there. It was the perfect place to work as an author, and I wrote many chapters there, usually powered by an Ethiopian single origin coffee from Caffenation. Merci papa et maman, for encouraging our free spirit. Finally, I want to thank my wife Yunshan, who’s always been supportive and encouraging throughout the writing of the book. Very importantly, you also made the book a more pleasant experience, thanks to your cute pictures! Chérie, your support was key, and I want to support your own projects the same way you have this book.

关于作者

About the Author

Cyrille Martraire (@cyriux on Twitter) 是 Arolla (@ArollaFr on Twitter) 的首席技术官、联合创始人和合伙人、巴黎 Software Crafters 社区的创始人以及国际会议上的定期演讲者。Cyrille 将自己称为开发人员,因为他自 1999 年以来一直以员工和顾问的身份为初创公司、软件供应商和企业设计软件。

Cyrille Martraire (@cyriux on Twitter) is CTO, co-founder, and partner at Arolla (@ArollaFr on Twitter), the founder of the Paris Software Crafters community, and a regular speaker at international conferences. Cyrille refers to himself as a developer, since he has designed software since 1999 for startups, software vendors, and corporations as an employee and as a consultant.

他曾工作并领导过多个重大项目,主要是资本融资领域,包括完全重写利率互换多边交易机制。在大多数情况下,他必须从大型且糟糕的遗留系统开始。

He has worked and led multiple significant projects, mostly in capital finance, including the complete rewriting of a multilateral trading facility of interest rate swaps. In most cases he has to start from large and miserable legacy systems.

他对软件设计的各个方面都充满热情:测试驱动开发、行为驱动开发,尤其是领域驱动设计。

He’s passionate about software design in every aspect: test-driven development, behavior-driven development, and, in particular, domain-driven design.

西里尔与妻子云山以及孩子诺伯特和古斯塔夫住在巴黎。

Cyrille lives in Paris with his wife, Yunshan, and children, Norbert and Gustave.

介绍

Introduction

我从来没有计划写一本关于活文档的书。我什至没有想到这个话题值得写一本书。

I never planned to write a book on living documentation. I didn’t even have in mind that this topic was worth a book.

很久以前,我有一个宏伟的梦想,即创建能够理解我们在编码时做出的设计决策的工具。几年来我花了很多空闲时间试图为此提出一个框架,却发现很难让这样一个框架适合每个人。然而,只要这个想法对我正在从事的项目有帮助,我就会尝试它。

Long ago, I had a grandiose dream of creating tools that could understand the design decisions we make when coding. I spent a lot of free time over several years trying to come up with a framework for that, only to find out it’s very hard to make such a framework suitable for everyone. However, I tried the idea whenever it was helpful in the projects I was working on.

2013 年,我在 Øredev 上发表了关于重构规范的演讲。在演讲结束时,我提到了我多年来一直在尝试的一些想法,我对收到的有关实时文档想法的热情反馈感到惊讶。就在那时,我认识到需要更好的方法来制作文档。从那时起,我已经多次做过这个演讲,反馈仍然是关于文档问题以及如何改进它,如何使其实时和自动化,而无需手动操作。

In 2013 I was speaking at Øredev on refactoring specifications. At the end of the talk I mentioned some of the ideas I’d been trying over time, and I was surprised at the enthusiastic feedback I received about the living documentation ideas. That is when I recognized the need for better ways to do documentation. I’ve done this talk other times since then, and the feedback has continued to be about the documentation thing and how to improve it, how to make it real-time and automated, without manual effort.

Gojko Adzic 在《实例化需求说明》一书中引入了术语“活文档”,作为实例化需求说明的诸多好处之一。但活文档是一个不限于规范的想法的好名字。

The term living documentation was introduced in the book Specification by Example by Gojko Adzic, as one of the many benefits of specification by example. But living documentation is a good name for an idea that is not limited to specifications.

关于活的文档,我有很多想法可以分享。我写下了我尝试过的所有这些事情的清单,以及我学到的有关该主题的其他内容。更多的想法来自其他人——我真正认识的人和我只从 Twitter 上认识的人。随着一切的发展,我决定把它写成一本书。我相信一本书会更有用,它可以帮助您创建快速且自定义的解决方案来制作您自己的实时文档,而不是提供一个可供使用的框架。

I had many ideas to share about living documentation. I wrote down a list of all these things I had tried, as well as other stuff I had learned about the topic. More ideas came from other people—people I actually know and people I know only from Twitter. As all that was growing, I decided to make it into a book. Instead of offering a framework ready for use, I believe a book will be more useful to help you create quick and custom solutions to make your own living documentation.

本书的内容

What This Book Is About

《实例化需求说明》一书介绍了活文档的概念,其中用于文档的行为示例被提升为自动化测试。每当测试失败时,您就知道文档不再与代码同步,并且您可以快速修复它。这个想法表明,有可能有用的文档不会一写出来就过时。但我们可以走得更远。

The book Specification by Example introduced the idea of living documentation, where an example of behavior used for documentation is promoted into automated test. Whenever the test fails, you know the documentation is no longer in sync with the code, and you can just fix it quickly. This idea has shown that it is possible to have useful documentation that doesn’t suffer the fate of becoming obsolete as soon as it is written. But we can go much further.

本书扩展了 Gojko 的活文档理念,包含一种与项目许多方面的代码同步发展的文档,从业务目标到业务领域知识、架构和设计、流程和部署。

This book expands on Gojko’s idea of living documentation, embracing a kind of documentation that evolves at the same pace as the code for many aspects of a project, from the business goals to the business domain knowledge, architecture and design, processes, and deployment.

本书结合了一些理论和实践,配有插图和具体例子。您将学习如何开始投资于始终保持最新且额外成本最小的文档,这要归功于精心制作的工件和合理的自动化程度。

This book combines some theory and practice, with illustrations and concrete examples. You will learn how to start investing in documentation that is always up to date and has minimal extra cost, thanks to well-crafted artifacts and a reasonable amount of automation.

您会发现您不必在工作软件和大量文档之间进行选择!

You will see that you don’t necessarily have to choose between working software and extensive documentation!

这本书适合谁

Who This Book Is For

本书主要面向软件开发人员或任何不害怕源代码控制系统中的代码的人。它以代码为中心,适用于开发人员、编码架构师和了解代码的高级角色。它还满足其他利益相关者(从业务分析师到经理)的一些需求,但通过更改源代码并将文件提交到源控制系统的软件开发人员的视角。

This book is primarily for software developers or anyone who is not afraid of code in a source control system. It is code-centric and meant for developers, coding architects, and those in senior roles who understand code. It also addresses some needs from other stakeholders, from business analysts to managers, but through the lens of software developers who change the source code and commit files into the source control system.

本书不是关于制作用户文档的。做好用户文档需要诸如技术写作之类的特定技能,但这绝对不是本书的主题。

This book is not about producing user documentation. Specific skills such as technical writing are required to do user documentation well, and that is absolutely not the topic of this book.

如何阅读本书

How to Read This Book

本书的主题是活文档,并以相关模式的网络形式呈现。每个模式都是独立的并且可以独立阅读。然而,为了充分理解每种模式并将其置于上下文中,通常还需要查看相关模式。在本书的网站上,您可以找到模式图,其中说明了模式之间的一些关系。

This book is on the topic of living documentation, and it is presented as a network of related patterns. Each pattern stands on its own and can be read independently. However, to fully understand and situate each pattern in context, it’s often desirable to also look at related patterns. On the book’s website, you can find pattern diagrams, which illustrate some of the relationships between the patterns.

本书的内容是按顺序组织的,从管理知识的问题,到BDD的启发,到一些初步理论,到知识变化的不同步伐和相应的文档技术。然后,它会扩展到此之外,重点关注架构和遗留系统的应用程序,以及如何在您的环境中引入实时文档。

The content of this book is organized as a progression from the problem of managing knowledge, to the inspiration from BDD, to some preliminary theory, to the different paces of knowledge changes and the corresponding documentation techniques. It then expands beyond that, focusing on the application for architecture and legacy systems, and on how to introduce living documentation in your environment.

我建议从第 1 章开始,确保您掌握了第 3 章和第 4 章的关键思想,然后再看5第 9章,其中涵盖了一般实用技术。那么你应该看看第十章来换个角度。第 11 章第 15章涵盖了更具体的主题并提供了更多示例。

I suggest starting with Chapter 1 and making sure you grasp the key ideas of Chapters 3 and 4 before taking a look at Chapters 5 through 9, which cover general practical techniques. Then you should take a look at the Chapter 10 for the shift of perspective. Chapters 11 through 15 cover more specific topics and provide additional examples.

有些读者喜欢从头到尾阅读这本书;有些读者则喜欢从头到尾读完这本书。但是,您可以随意浏览、深入研究特定区域,并以任何其他顺序阅读文本。

Some readers have enjoyed reading this book cover to cover; however, feel free to skim, dig into a particular area, and read the text in any other order.

本书涵盖的内容

What This Book Covers

第 1 章重新思考文档”从第一原则重新审视文档,为本书的其余部分奠定了基础。

Chapter 1, “Rethinking Documentation,” takes a fresh look at documentation from first principles, providing a foundation for the rest of the book.

第 2 章行为驱动开发作为实时规范的示例”描述了 BDD 如何成为实时文档背后的关键灵感,尽管 BDD 本身并不是本书的中心主题。

Chapter 2, “Behavior-Driven Development as an Example of Living Specifications,” describes how BDD is a key inspiration behind living documentation, although BDD itself is not the central topic of this book.

第 3 章知识利用”和第 4 章“知识增强”为其他实践奠定了基础。特别是,他们讨论了提取现有的知识并用缺失的知识来补充它。

Chapter 3, “Knowledge Exploitation,” and Chapter 4, “Knowledge Augmentation,” lay a foundation on which other practices build. In particular, they discuss extracting knowledge that is there and augmenting it with what’s missing.

第五章生活策展:识别权威知识”展示了如何开始通过策展将知识转化为有用的东西,同时接受这些知识在不断变化。

Chapter 5, “Living Curation: Identifying Authoritative Knowledge,” shows how to start turning knowledge into something useful through curation, while embracing that this knowledge is continuously changing.

第 6 章自动化文档”详细介绍了将知识转变为与知识同步的文档和图表,反映了知识中的每次变化。

Chapter 6, “Automating Documentation,” expands on turning the knowledge into documentation and diagrams that live at the pace of the knowledge, mirroring each change in the knowledge.

第 7 章运行时文档”是前一章的扩展,讨论如何使用只能在运行时访问的知识。

Chapter 7, “Runtime Documentation,” is an extension of the previous chapter, discussing how to use knowledge that is accessible only at runtime.

第 8 章可重构文档”更加以代码为中心,重点介绍使用开发工具来帮助保持文档最新。

Chapter 8, “Refactorable Documentation,” is more code-centric and focuses on using development tools to help keep documentation up-to-date.

与前面的章节相比,第 9 章稳定的文档”探讨了这样的想法:对于不会改变的知识,不需要活的技术,并讨论记录这些知识的更好方法。

In contrast to the previous chapters, Chapter 9, “Stable Documentation,” explores the idea that you don’t need living techniques for knowledge that doesn’t change and discusses better methods of documenting such knowledge.

第 10 章避免传统文档”采用了更加反叛的视角,重点关注替代性文档方法。

Chapter 10, “Avoiding Traditional Documentation,” takes a more rebellious perspective, with a focus on alternative means of documentation.

在所有关于如何通过设计改进文档的章节之后,第 11 章超越文档:生动的设计”采取了不同的观点:对文档的关注如何帮助您改进设计本身。

After all the chapters on how to improve documentation by design, Chapter 11, “Beyond Documentation: Living Design,” takes a different view: how a focus on documentation could help you improve the design itself.

第 12 章动态架构文档”将活文档思想应用于软件架构并讨论一些具体技术。

Chapter 12, “Living Architecture Documentation,” applies the living documentation ideas to software architecture and discusses some specific techniques.

第 13 章将活文档引入新环境”提供了有关如何在您的环境中引入活文档的指导,主要是作为一项社会挑战。

Chapter 13, “Introducing Living Documentation to a New Environment,” offers guidance on how to introduce living documentation in your environment, mostly as a social challenge.

因为我们被遗留系统所包围,所以第 14 章记录遗留应用程序”以一组用于处理这些严峻的遗留挑战的特定模式来结束这本书。

Because we are surrounded by legacy systems, Chapter 14, “Documenting Legacy Applications,” closes the book with a set of specific patterns for dealing with these tough legacy challenges.

第 15 章额外:引人注目的文档”是关于实用建议的额外章节,旨在通过使所有活文档计划更加引人注目来使它们更加有效。

Chapter 15, “Extra: Conspicuous Documentation,” is a bonus chapter on practical advice to make all your living documentation initiatives more effective by making them more noticeable.

注册您的书

Register Your Book

在 informit.com 上注册您的Living Documentation副本,以便在可用时方便地进行下载、更新和更正。要开始注册过程,请访问 informit.com/register 并登录或创建帐户。输入产品 ISBN 9780134689326,然后单击“提交”。该过程完成后,您将在“注册产品”下找到任何可用的奖励内容。

Register your copy of Living Documentation at informit.com for convenient access to downloads, updates, and corrections as they become available. To start the registration process, go to informit.com/register and log in or create an account. Enter the product ISBN 9780134689326 and click Submit. Once the process is complete, you will find any available bonus content under “Registered Products.”

人物学分

Figure Credits

封面图片:夏云山

Cover image: Yunshan Xia

全球范围内,LOL笔记图片:夏云山

Globally, LOL note image: Yunshan Xia

图1.1至1.6、1.101.11 云山

Figures 1.1 through 1.6, 1.10 and 1.11: Yunshan Xia

图1.9:夏云山

Figure 1.9: Yunshan Xia

图2.1:夏云山

Figure 2.1: Yunshan Xia

图 2.4:Pickles 的屏幕截图 © Jeffrey Cameron 2011-2012

Figure 2.4: Screenshot of Pickles © Jeffrey Cameron 2011-2012

3.3、3.5、3.8至3.11 夏云山_

Figures 3.3, 3.5, 3.8 through 3.11: Yunshan Xia

图 4.34.4:夏云山

Figures 4.3 and 4.4: Yunshan Xia

图 4.5:Github 的屏幕截图 © 2019 GitHub, Inc.

Figure 4.5: Screenshot of Github © 2019 GitHub, Inc.

图 5.15.4:夏云山

Figures 5.1 and 5.4: Yunshan Xia

图 5.2:Eclipse 的屏幕截图 © Eclipse Foundation, Inc.

Figure 5.2: Screenshot of Eclipse © Eclipse Foundation, Inc.

图 6.2:Java 的屏幕截图 © Oracle

Figure 6.2: Screenshot of Java © Oracle

图6.7:夏云山

Figure 6.7: Yunshan Xia

图 6.10:阿利斯泰尔·科伯恩

Figure 6.10: Alistair Cockburn

图 6.15:Java 的屏幕截图 © Oracle

Figure 6.15: Screenshot of Java © Oracle

图 7.1:Zipkin 截图 © Zipkin 2019

Figure 7.1: Screenshot of Zipkin © Zipkin 2019

图 8.5:Eclipse 的屏幕截图 © Eclipse Foundation, Inc.

Figure 8.5: Screenshot of Eclipse © Eclipse Foundation, Inc.

图 8.6:西里尔·马特莱尔

Figure 8.6: Cyrille Martraire

10.1、10.5、10.9 夏云山_

Figure 10.1, 10.5, and 10.9: Yunshan Xia

图 10.2:阿利斯泰尔·科伯恩

Figure 10.2: Alistair Cockburn

图 10.3:西里尔·马特莱尔

Figure 10.3: Cyrille Martraire

图 10.6:Bug Magnet 的屏幕截图 © 2015-2017 Gojko Adzic

Figure 10.6: Screenshot of Bug Magnet © 2015-2017 Gojko Adzic

图 10.8:约翰·阿伦德尔,http://bitfieldconsulting.com/puppet-dependency-graphs

Figure 10.8: John Arundel, http://bitfieldconsulting.com/puppet-dependency-graphs

11.1、11.2、11.4、11.7 云山_ _ _

Figures 11.1, 11.2, 11.4, and 11.7: Yunshan Xia

图 11.511.6:词云截图 © Zygomatic

Figure 11.5 and 11.6: Screenshot of Word Cloud © Zygomatic

12.3、12.4、12.5 夏云山_

Figures 12.3, 12.4, and 12.5: Yunshan Xia

图 15.1、15.4和15.5 Cyrille Martraire

Figures 15.1, 15.4, and 15.5: Cyrille Martraire

图15.215.3:夏云山

Figures 15.2 and 15.3: Yunshan Xia

第1章

Chapter 1

重新思考文档

Rethinking Documentation

忘记文档。相反,应该关注软件的工作速度。您希望更快地交付软件。这不仅仅是现在的快速发展,而是从长远来看可持续的快速发展。这不仅仅是你要快速前进,而是整个团队或公司要快速前进。

Forget about documentation. Instead, focus on the speed of working on software. You want to deliver software faster. It isn’t just about going fast right now but about going sustainably fast in the long run. It is not just about you going fast but about the whole team or company going fast.

更快地开发软件需要更高效的编程语言和框架、更好的工具以及更高水平的技能。但行业在这些方面取得的进步越多,我们就越要审视其他瓶颈。

Working on software faster involves more productive programming languages and frameworks, better tools, and higher level of skills. But the more the industry makes progress on all these aspects, the more we have to look at the other bottlenecks.

除了利用技术之外,编写软件很大程度上是基于知识做出决策。当你没有足够的知识时,你必须进行学习实验并与其他人合作来发现新知识。这需要时间,这也意味着这些知识是昂贵且有价值的。快速学习是指当您需要新知识时更快地学习,或者是在有任何先前有价值的知识时快速恢复任何有价值的知识。让我们用一个小故事来说明这一点。

Beyond making use of technology, writing software is a lot about making decisions based on knowledge. When you don’t have enough knowledge, you have to make learning experiments and collaborate with other people to discover new knowledge. This takes time, which also means this knowledge is expensive and has value. Going fast is all about learning faster when you need new knowledge or about quickly recovering any prior valuable knowledge whenever there was some. Let us illustrate that point with a little story.

活生生的土地上的故事

A Tale from the Land of Living Documentation

我们从一个故事开始。想象一个软件项目,用于开发一个新应用程序,作为您公司更大的信息系统的一部分。想象一下您是这个项目的开发人员。您的任务是为最近的忠实客户添加一种新的折扣。

We begin with a story. Imagine a software project to develop a new application as part of a bigger information system in your company. And imagine that you are a developer on this project. Your task is to add a new kind of discount for recent loyal customers.

为什么有这个功能?

Why This Feature?

您遇到了来自营销团队的 Franck 和专业测试人员 Lisa,三人开始讨论新功能、提出问题并寻找具体示例。丽莎问:“为什么有这个功能?” Franck 解释说,其基本原理是通过游戏化方法奖励最近的忠实客户,以提高客户保留率,并建议在维基百科上提供有关该主题的链接。丽莎对要点和主要场景做了一些笔记。

You meet Franck, from the marketing team, and Lisa, a professional tester, and the three of you start talking about the new feature, asking questions, and looking for concrete examples. Lisa asks, “Why this feature?” Franck explains that the rationale is to reward recent loyal customers in order to increase customer retention, in a gamification approach, and suggests a link on Wikipedia about that topic. Lisa takes some notes about the main points and main scenarios.

所有这一切都进行得很快,因为每个人都围坐在桌子旁,而且沟通很容易。此外,具体的例子使人们更容易理解和澄清任何开始不清楚的事情。一旦一切都清楚了,每个人都会回到自己的办公桌上。现在轮到丽莎写下最重要的场景并将其发送给每个人。(上次轮到 Franck 了。)现在您可以从这里开始编码。

All this goes quickly because everyone is around the table, and communication is easy. Also, the concrete examples make it easier to understand and clarify anything that starts out unclear. Once it’s all clear, everyone gets back to their desk. It’s Lisa’s turn to write down the most important scenarios and send them to everyone. (Last time it was Franck’s turn.) Now you can start coding from that.

在你以前的工作经验中,这个过程并不是这样进行的。团队通过充满歧义、难以阅读的文档相互交谈。你微笑。您快速将第一个场景转变为自动化验收测试,观察它失败,然后开始编写代码以使其通过绿色测试。

In your previous work experience, the process did not work like this. Teams talked to each other through hard-to-read documents full of ambiguities. You smile. You quickly turn the first scenario into an automated acceptance test, watch it fail, and start writing code to make it pass to green.

你有一种美好的感觉,你将能够把宝贵的时间花在重要的事情上,而不是别的。

You have the nice feeling that you’ll be able to spend your valuable time on what matters and nothing else.

明天你将不再需要这张草图

Tomorrow You Won’t Need This Sketch Anymore

那天下午,两位同事乔治和埃丝特向团队询问需要做出的设计决策。你们在白板周围会面,并在绘制草图时快速评估每个选项。此时您不需要太多 UML 1 ,只需要一些自定义框和箭头。您只想确保每个人现在都理解它。几分钟后,选择了一个解决方案。该计划是在消息系统中使用两个不同的主题;做出这一决定的理由是需要在收到的订单和装运请求之间进行完全隔离。

That afternoon, a pair of colleagues, Georges and Esther, ask the team about a design decision that needs to be made. You meet around the whiteboard and quickly evaluate each option while sketching. You don’t need much UML1 at this point, just some custom boxes and arrows. You just want to make sure everybody understands it right now. A few minutes later a solution is chosen. The plan is to use two different topics in the messaging system; the rationale for this decision is the need for full isolation between the incoming orders and the shipment requests.

1.统一建模语言: http://www.uml.org/

1.Unified Modeling Language: http://www.uml.org/

Esther 用手机给白板拍了一张照片,以防有人在白天擦掉白板。但她知道,半天之内,就会实施,然后她就可以安全地删除手机里存储的图片了。一小时后,当她提交创建新的消息传递主题时,她小心翼翼地在提交注释中添加了“传入订单和发货请求之间的隔离”的基本原理。

Esther takes a picture of the whiteboard with her phone, just in case someone erases the whiteboard during the day. But she knows that in half a day, it will be implemented, and she can then safely delete the picture stored in her phone. One hour later, when she commits the creation of the new messaging topic, she takes care to add the rationale “isolation between incoming orders and shipment requests” in the commit comment.

第二天,昨天不在的德拉戈斯注意到了新代码,并想知道为什么会这样。他git blame上线后立即得到了答案。

The next day, Dragos, who was away yesterday, notices the new code and wonders why it’s like that. He runs git blame on the line and immediately gets the answer.

抱歉,我们没有营销文件!

Sorry, We Don’t Have Marketing Documents!

一周后,新的营销经理米歇尔接替了弗兰克。米歇尔比弗兰克更注重保留客户。她想知道应用程序在客户保留方面已经实施了哪些措施,因此她要求提供相应的营销文档,但她惊讶地发现没有。

A week later, a new marketing manager, Michelle, replaces Franck. Michelle is more into customer retention than Franck. She wants to know what’s already implemented in the application in the area of customer retention, so she asks for the corresponding marketing document, and she is surprised to learn there is none.

“你不可能是认真的!” 她首先说。但您很快就向她展示了网站,其中包含每次构建期间生成的所有验收测试。顶部有一个搜索区域,因此她可以输入“客户保留”并进行搜索。她单击“提交”并发现结果:

“You can’t be serious!” she first says. But you quickly show her the website with all the acceptance tests produced during each build. There’s a search area on top so she can enter “customer retention” and search on it. She clicks submit and discovers the results:

1 为了提高客户保留率
2 作为营销人员
3 我想为最近的忠实客户提供折扣
4
5 场景:近期忠诚客户下次购买可享受 10 美元折扣
顾客
6 ...
\7
8 场景:最近的忠实客户在该网站购买过 3 次
上星期
9 ...
1  In order to increase customer retention
2  As a marketing person
3  I want to offer a discount to recent loyal customers
4
5  Scenario: 10$ off on next purchase for recent loyal
customer
6  ...
\7
8  Scenario: Recent loyal customers have bought 3 times in the
last week
9  ...

结果列表显示了许多有关最近忠实客户的特别折扣的场景。米歇尔微笑着。她甚至不必浏览营销文档即可找到她正在寻找的知识。而且这些场景的精确程度远远超出了她的预期。

The result list displays many scenarios about the special discount for recent loyal customers. Michelle smiles. She didn’t even have to browse a marketing document to find the knowledge she was looking for. And the level of precision of these scenarios well exceeds what she was expected.

“我们可以为用欧元购买商品提供同样的折扣吗?” 米歇尔问道。你回答说:“我不确定代码能否很好地管理货币,但我们试试吧。” 在 IDE 2中,您更改验收测试中的货币,然后再次运行测试。他们失败了,所以你知道需要做一些工作来支持这一点。米歇尔几分钟内就得到了答案。她开始认为你的团队与她以前的工作环境相比有一些特别之处。

“Could we do the same discount for purchases in euro?” Michelle asks. You reply, “I’m not sure the code manages currencies well, but let’s just try.” In your IDE2, you change the currency in the acceptance test, and you run the tests again. They fail, so you know some work will need to be done to support that. Michelle has her answer within minutes. She begins to think that your team has something special compared to her former work environments.

2.集成开发环境(IDE)。

2.integrated development environment (IDE).

你一直用这个词,但这不是它的意思

You Keep Using This Word, but This Is Not What It Means

第二天,米歇尔又提出了一个问题:购买订单有什么区别?

The next day Michelle has another question: What is the difference between a purchase and an order?

通常她只会要求开发人员查看代码并解释其中的差异。然而,该团队已经预料到了这个问题,并且该项目的网站包含了一个术语表。“这个术语表是最新的吗?” 她问。“是的,”你回答道。“它会在每次构建期间自动从代码中更新。” 她很惊讶。为什么不是每个人都这样做呢?“为此,你需要让你的代码与业务领域紧密结合”,你简单地说,尽管你很想详细说明你非常热衷的 Eric Evans 的《领域驱动设计 3》一书中的无处不在语言

Usually she would just ask the developers to look in the code and explain the difference. However, this team has anticipated the question, and the project’s website includes a glossary. “Is this glossary up-to-date?” she asks. “Yes,” you reply. “It’s updated during every build—automatically from the code.” She’s surprised. Why doesn’t everybody do that? “You need to have your code closely in line with the business domain for that,” you say simply, though you’re tempted to elaborate on the ubiquitous language from Eric Evans’s book Domain-Driven Design3 you’re so enthusiastic about.

3.埃文斯,埃里克。领域驱动设计:解决软件核心的复杂性。霍博肯:Addison-Wesley Professional,2003。

3.Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. Hoboken: Addison-Wesley Professional, 2003.

查看术语表时,米歇尔发现了命名中以前没有人发现的混乱,她建议用正确的名称修复术语表。但这不是这里的工作方式。您首先要在代码中修复名称。您重命名该类并再次运行构建,瞧,术语表现在也已修复。每个人都很高兴,你刚刚学到了一些关于电子商务业务的新知识。

Looking at the glossary, Michelle discovers a confusion that nobody has spotted before in the naming, and she suggests fixing the glossary with the correct name. But this is not the way it works here. You want to fix the name first and foremost in the code. You rename the class and run the build again, and—voilà—the glossary is now fixed as well. Everybody is happy, and you just learned something new about the business of e-commerce.

给我看大局,你就会发现哪里出了问题

Show Me the Big Picture, and You’ll See What’s Wrong There

现在您想删除两个模块之间的有毒依赖关系,但您对完整的代码库不是很熟悉,因此您向 Esther 索要依赖关系图,因为她对此最了解。但即使是她也不记得每一个依赖。“我将根据代码生成依赖关系图。这是我一直想做的事情。这将花费我几个小时,但之后就永远完成了,”她说。

Now you’d like to remove a toxic dependency between two modules, but you’re not very familiar with the full codebase, so you ask Esther for a dependency diagram, since she has the most knowledge of that. But even she does not remember every dependency. “I’ll generate a diagram of the dependencies from the code. It’s something I’ve long wanted to do. This will take me a few hours, but then it’ll be done forever,” she says.

Esther 已经了解了一些开源库,她可以使用这些库轻松地从类或包中提取依赖项,并且她很快将其中一个连接到 Graphviz,这是一款自动进行布局的神奇图表生成器。几个小时后,她的小工具生成了依赖关系图。你得到了你想要的,你很高兴。然后,她额外花了半个小时将此工具集成到构建中。

Esther already knows about a few open-source libraries she can use to easily extract the dependencies from a class or a package, and she quickly wires one to Graphviz, the magical diagram generator that does the layout automatically. A few hours later, her little tool generates the diagram of dependencies. You get what you wanted, and you’re happy. She then spends an extra half hour integrating this tool into the build.

但有趣的是,当 Esther 第一次查看生成的图表时,她注意到一些有趣的事情:两个模块之间不应该存在的依赖关系。通过将她脑海中的系统视图与生成的实际系统视图进行比较,很容易发现设计缺陷。

But the funny thing is that when Esther first looks at the generated diagram, she notices something intriguing: a dependency between the two modules that should not be there. By comparing her mental view of the system with the generated view of the actual system, it was easy to spot the design weakness.

在下一个项目迭代中,设计弱点得到修复,并且在下一个构建中,依赖关系图会自动更新。它变得更加清晰。

In the next project iteration, the design weakness is fixed, and in the next build, the dependency diagram is automatically updated. It becomes a much cleaner diagram.

活文档的未来就在眼前

The Future of Living Documentation Is Now

这个故事与未来无关。它已经在这里了,现在,而且已经在这里很多年了。用小说作家威廉·吉布森的话来说,“未来已经到来,只是分布尚未均匀”。

This tale is not about the future. It is already here, right now, and it has been here for years already. To quote fiction writer William Gibson, this “future has arrived, it’s just not evenly distributed yet.”

工具在这里。技巧就在这里。人们多年来一直在这样做,但它还不是主流。这很遗憾,因为这些对于软件开发团队来说是强大的想法。

The tools are here. The techniques are here. People have been doing all this for ages, but it’s not yet mainstream. That’s a pity because these are powerful ideas for software development teams.

在接下来的章节中,我们将介绍所有这些方法和许多其他方法,您将学习如何在项目中实现它们。

In the following chapters, we’ll go through all these approaches and many others, and you’ll learn how to implement them in your projects.

传统文档的问题

The Problem with Traditional Documentation

文档是编程的蓖麻油——经理认为它对程序员有好处,但程序员却讨厌它!

Documentation is the castor oil of programming—managers think it is good for programmers, and programmers hate it!

——杰拉尔德·温伯格,计算机编程心理学

—Gerald Weinberg, Psychology of Computer Programming

文档是一个无聊的话题。我不了解你的情况,但根据我迄今为止的工作经验,文档大多是令人沮丧的一个重要原因。

Documentation is a boring topic. I don’t know about you, but in my work experience so far, documentation has mostly been a great source of frustration.

当我尝试使用文档时,我需要的信息总是丢失。当它存在时,它通常已经过时且具有误导性,所以我什至不能相信它。

When I’m trying to consume documentation, the information I need is always missing. When it’s there, it is often obsolete and misleading, so I can’t even trust it.

为其他人创建文档是一项无聊的任务,我更愿意编码。但事实并非一定如此。

Creating documentation for other people is a boring task, and I’d prefer to be coding instead. But it does not have to be this way.

我曾多次看到、使用或听说过处理文档的更好方法。我已经尝试了很多。我收集了很多故事,你会在这本书中找到其中的许多故事。

There have been a number of times when I’ve seen, used, or heard about better ways to deal with documentation. I’ve tried a lot of them. I’ve collected a number of stories, and you’ll find many of them in this book.

有更好的方法,但它需要对文档采取新的思维方式。有了这种心态和与之相伴的技术,文档就有可能像编码一样有趣。

There’s a better way, but it requires adopting a new mindset about documentation. With this mindset and the techniques that go with it, it is possible for documentation to be as much fun as coding.

通常,文档并不酷

Documentation Is Not Cool, Usually

当您听到文档这个词时,您会想到什么?以下是您可能给出的一些答案:

What comes to mind when you hear the word documentation? Here are a few of the answers you might give:

  • 这很无聊。

  • It’s boring.

  • 它涉及编写大量文本。

  • It involves writing lots of text.

  • 这意味着尝试使用 Microsoft Word,同时又不失去对图片放置的理智。

  • It means trying to use Microsoft Word without losing your sanity with picture placement.

  • 作为一名开发人员,我喜欢展示运动和行为的动态、可执行的东西。对我来说,文档就像一株静止且干燥的死植物。

  • As a developer, I love dynamic, executable stuff that exhibits motion and behavior. To me, documentation is like a dead plant that’s static and dry.

  • 它应该是有帮助的,但它经常会产生误导。

  • It’s supposed to be helpful, but it’s often misleading.

  • 创建文档是一件无聊的苦差事。我更喜欢编写代码而不是编写文档(参见图 1.1)!

  • Creating documentation is a boring chore. I’d prefer to be writing code instead of doing documentation (see Figure 1.1)!

图中显示了一个卡通人物更喜欢编写代码而不是编写文档。

图 1.1 哦不……我最好编码!

Figure 1.1 Oh no…I’d better be coding!

文档需要花费大量时间来编写和维护,它很快就会过时,通常充其量是不完整的,而且一点也不有趣。文档是令人沮丧的一个极好的来源。我很抱歉让您就这样一个无聊的话题进行这次旅程。

Documentation takes a lot of time to write and to maintain, it becomes obsolete quickly, it is typically incomplete at best, and it is just not fun. Documentation is a fantastic source of frustration. And I’m sorry to bring you on this journey on such a dull topic.

文档的缺陷

The Flaws of Documentation

就像廉价的葡萄酒一样,纸质文档会迅速老化并让您头疼不已。

Like cheap wine, paper documentation ages rapidly and leaves you with a bad headache.

—推特上的@gojkoadzic

—@gojkoadzic on Twitter

传统文档存在许多缺陷和一些常见的反模式。反模式描述了对重复出现的问题的常见响应,该响应被认为不是一个好主意,应该避免。

Traditional documentation suffers from many flaws and several common anti-patterns. An anti-pattern describes a common response to a recurring problem that is considered not a good idea and that should avoided.

以下各节描述了一些最常见的文档缺陷和反模式。您在自己的项目中认识其中的一些吗?

Some of the most frequent flaws and anti-patterns of documentation are described in the following sections. Do you recognize some of them in your own projects?

单独的活动

即使在声称敏捷的软件开发项目中,决定构建什么以及进行编码、测试和准备文档也常常是独立的活动,如图 1.2所示

Even in software development projects that claim to be agile, deciding what to build and doing the coding, testing, and preparing documentation are too often separate activities, as illustrated in Figure 1.2.

下图说明了软件开发项目中的单独活动。 该图分别显示了软件开发过程中涉及的卡通人物,例如物种形成、编码、测试和文档记录。

图 1.2 软件开发项目中的单独活动

Figure 1.2 Separate activities in software development projects

单独的活动会导致大量浪费和失去机会。基本上,在每项活动中都会操纵相同的知识,但以不同的形式和方式在不同的工件中——并且可能有一定数量的重复。此外,这种“相同”的知识可能会在过程本身中演变,这可能会导致不一致。

Separate activities induce a lot of waste and lost opportunities. Basically, the same knowledge is manipulated during each activity, but in different forms and in different artifacts—and probably with some amount of duplication. In addition, this “same” knowledge can evolve during the process itself, which may cause inconsistencies.

手动转录

当需要制作文档时,团队成员会选择一些已完成工作的知识元素,并手动转录为适合预期受众的格式。基本上,这意味着编写另一份关于代码中刚刚完成的操作的文档,就像古腾堡之前的抄写员一样(参见图 1.3)。

When the time comes to do documentation, members of the team select some elements of knowledge of what has been done and perform a manual transcription into a format suitable for the expected audience. Basically, it means writing another document about what has just been done in the code—like copyists before Gutenberg (see Figure 1.3).

图为正在手动转录的卡通人物。

图1.3 手动转录

Figure 1.3 Manual transcription

冗余知识

刚刚描述的转录会导致冗余知识:您最终会得到原始的事实来源(通常是代码)和一堆以各种形式复制这些知识的副本。不幸的是,当一个工件发生变化(例如代码)时,很难记住更新其他文档。因此,文档很快就会过时,并且最终会得到无法信任的不完整文档。该文档有多大用处?

The transcription just described leads to redundant knowledge: You end up with the original source of truth (usually the code) and a bunch of copies that duplicate this knowledge in various forms. Unfortunately, when one artifact changes—for example, the code—it is hard to remember to update the other documents. As a result, the documentation quickly becomes obsolete, and you end up with incomplete documentation that you cannot trust. How useful is that documentation?

无聊的时间沉没

经理们需要为用户提供文档,并应对团队的人员流动。然而,开发人员讨厌编写文档。与编写代码或自动化任务相比,这并不有趣。对于开发人员来说,快速过时且无法执行的死文本并不是特别令人兴奋。当开发人员处理文档时,他们更愿意处理真正可以工作的软件。矛盾的是,当他们想要重用第三方软件时,他们常常希望它有更多可用的文档。

Managers want documentation for the users and also to cope with the turnover in the team. However, developers hate writing documentation. It is not fun compared to writing code or compared to automating a task. Dead text that gets obsolete quickly and that does not execute is not particularly exciting to write for a developer. When developers are working on documentation, they’d prefer to be working on the real working software instead. Paradoxically, when they want to reuse third-party software, they often wish it had more documentation available.

技术作家喜欢编写文档并为此付费。然而,为了获得所需的技术知识,他们通常需要开发人员,而且通常他们仍在手动转录知识。这一切都很令人沮丧,并且消耗了大量宝贵的时间(见图1.4)。

Technical writers like to do documentation and are paid for that. However, to get access to the technical knowledge required, they usually need developers, and often they’re still doing manual transcription of knowledge. This is all frustrating and consumes a lot of precious time (see Figure 1.4).

图中显示了一个手里拿着时钟的卡通人物。

图 1.4 文档通常会浪费时间

Figure 1.4 Documentation is often a time sink

大脑转储

因为编写文档并不有趣,而且只是因为必须要做而完成,所以常常是随意完成的,没有经过太多思考。结果是随机的作者在写作时的想法(见图1.5)。问题是这样的随机大脑转储对任何人都没有帮助。

Because writing documentation is not fun and is done because it simply has to be done, it is often done arbitrarily, without much thinking. The result is a random brain dump of what the writer had in mind at the time of writing (see Figure 1.5). The problem is that such a random brain dump is not helpful to anyone.

图中显示了一个卡通人物,说明大脑转储不一定有用作为文档。

图 1.5 大脑转储不一定有用作为文档

Figure 1.5 A brain dump is not necessarily useful as documentation

抛光图

这种反模式对于喜欢使用 CASE 工具的人来说很常见。这些工具不适用于绘制草图。相反,他们鼓励创建精美的大型图表,并根据建模参考进行各种布局和验证。这一切都需要很多时间。即使使用这些工具的所有自动神奇布局功能,创建一个简单的图表仍然需要太多时间。

This anti-pattern is common with people who like to use CASE tools. These tools are not meant for sketching. Instead they encourage the creation of polished and large diagrams, with various layouts and validation against a modeling referential. All this takes a lot of time. Even with all the auto-magical layout features of these tools, it still takes too much time to create even a simple diagram.

符号痴迷

现在越来越明显的是,UML 表示法不再流行,但在 1997 年采用它作为标准之后的十年里,它成为所有软件的通用表示法,尽管并不适合所有情况。从那时起,没有其他符号得到普及,世界各地的团队仍然使用一些 UML 来记录内容,即使它不太适合这样做。当您所知道的只是 UML 时,一切看起来都像是它的标准图表集合之一。

It is now increasingly obvious that the UML notation is not fashionable anymore, but in the decade following its adoption as a standard in 1997, it was the universal notation for everything software, despite not being suited for all situations. No other notation has been popularized since that time, and teams around the world still use some UML to document stuff, even when it is not well suited for that. When all you know is UML, everything looks like one of its collection of standard diagrams.

无符号

事实上,与符号迷恋相反的观点相当流行。许多人只是忽略了 UML,用没有人能以同样的方式理解的自定义符号绘制图表,并将构建依赖关系、数据流和部署问题等随机问题混合在一起。

In fact, the opposite of notation obsession has been rather popular. Many people have simply ignored UML, drawing diagrams with custom notations that nobody understands the same way and mixing together random concerns like build dependencies, data flow, and deployment concerns in a happy mess.

信息墓地

企业知识管理解决方案是知识消亡的地方。考虑这些:

Enterprise knowledge management solutions are the places where knowledge goes to die. Consider these:

  • 企业维基百科

  • Enterprise wikis

  • 共享点

  • SharePoint

  • 大型 Microsoft Office 文档

  • Large Microsoft Office documents

  • 共享文件夹

  • Shared folders

  • 搜索功能较差的票务系统和 wiki

  • Ticketing systems and wikis with poor search capabilities

这些文档方法常常会失败,要么是因为它们使找到正确的信息变得太困难,要么是因为保持信息最新需要太多工作,或者两者兼而有之。他们提倡一种只写文档一次写入文档的形式。

These approaches to documentations often fail either because they make it too hard to find the right information or because it’s too much work to keep the information up-to-date or both. They promote a form of write-only documentation, or write-once documentation.

在最近的 Twitter 交流中,著名软件开发者 Tim Ottinger (@tottinge) 问道:

On a recent Twitter exchange, the famous software developer Tim Ottinger (@tottinge) asked:

产品类别:“文档墓地”——所有文档管理、wiki、SharePoint 和团队空间都注定要失败吗?

Product category: “Document Graveyard” - are all document management & wiki & SharePoint & team spaces doomed?

詹姆斯·R·霍姆斯 (@James_R_Holmes) 回复:

James R. Holmes (@James_R_Holmes) replied:

我们的标准笑话是,“它在内联网上”会导致这样的反应:“你刚刚告诉我自己去____吗?”

Our standard joke is that “It’s on the intranet” leads to the response, “Did you just tell me to go ____ myself?”

(注:由于原始语言粗糙而进行了编辑;您明白了。)

(Note: Edited because of the original rough language; you get the idea.)

误导性帮助

只要文档没有严格保持最新,就会产生误导,如图1.6所示。虽然它假装有帮助,但这是错误的。因此,此类文档读起来可能很有趣,但试图找出哪些内容仍然正确、哪些内容变得不正确会带来额外的认知负担。

Whenever documentation is not strictly kept up-to-date, it becomes misleading, as pictured in Figure 1.6. Although it pretends to help, it is incorrect. As a result, such documentation may be interesting to read, but there’s an additional cognitive load involved in trying to find out what’s still correct versus what’s become incorrect.

图中显示了带有名为披萨的食品封面的寿司。

图 1.6 具有误导性的文档可能是有毒的

Figure 1.6 Documentation can be toxic when misleading

现在总有更重要的事情

编写好的文档需要大量的时间,而维护它则需要更多的时间。那些面临时间压力的人经常会跳过文档任务,或者做得又快又差。

Writing good documentation requires a lot of time, and maintaining it takes even more time. Those who are under time pressure often skip documentation tasks or do them quickly and badly.

敏捷宣言和文档

The Agile Manifesto and Documentation

敏捷宣言由一群软件从业者于 2001 年撰写。在其中,他们列出了他们所重视的内容,包括以下内容:

The Agile Manifesto was written by a group of software practitioners in 2001. In it, they list what they have come to value, including the following:

  • 流程和工具上的个体和交互

  • Individuals and interactions over processes and tools

  • 工作软件胜过全面的文档

  • Working software over comprehensive documentation

  • 客户协作胜过合同谈判

  • Customer collaboration over contract negotiation

  • 响应变化而不是遵循计划

  • Responding to change over following a plan

第二个偏好,“工作软件胜过全面文档”,经常被误解。许多人认为它完全无视文档。事实上,敏捷宣言并没有说“不要做文档”。这只是一个偏好问题。用宣言作者的话来说,“我们拥抱文档,但不把大量纸张浪费在从未维护和很少使用的大部头书籍中。” 4尽管如此,随着敏捷方法成为大公司的主流,误解仍然存在,许多人忽视了文档。

The second preference, “Working software over comprehensive documentation,” is frequently misunderstood. Many people believe that it disregards documentation completely. In fact, the Agile Manifesto does not say “don’t do documentation.” It’s only a matter of preference. In the words of the authors of the manifesto, “We embrace documentation, but not to waste reams of paper in never-maintained and rarely-used tomes.”4 Still, with agile approaches becoming mainstream in larger corporations, the misunderstanding is still there, and many people neglect documentation.

4.马丁·福勒和吉姆·海史密斯, http://agilemanifesto.org/history.html

4.Martin Fowler and Jim Highsmith, http://agilemanifesto.org/history.html

然而,我最近注意到,缺乏文档是我的客户和同事感到沮丧的一大根源,而且这种沮丧感越来越大。2013 年在瑞典举行的 Öredev 会议上,我首次提到活文档后,我很惊讶地发现人们对文档主题抱有浓厚的兴趣。

However, I’ve noticed recently that the lack of documentation is a big source of frustration for my customers and colleagues, and this frustration is getting bigger. I was surprised to see some great appetite for the topic of documentation after I first mentioned living documentation at the Öredev conference in Sweden in 2013.

现在是文档 2.0 的时候了

It’s Time for Documentation 2.0

传统的文档存在缺陷,但现在我们知道得更多了。自 20 世纪 90 年代末以来,干净代码、测试驱动开发 (TDD)、行为驱动开发 (BDD)、领域驱动设计 (DDD) 和持续交付等实践已变得越来越受欢迎。所有这些实践都改变了我们对交付软件的思考方式。

Traditional documentation is flawed, but now we know better. Since the end of the 1990s, practices like clean code, test-driven development (TDD), behavior-driven development (BDD), domain-driven design (DDD), and continuous delivery have become increasingly popular. All these practices have changed the way we think about delivering software.

对于 TDD,测试首先被视为规范。通过 DDD,我们可以识别业务领域的代码和建模,打破了模型与代码分开保存的传统。结果之一是我们期望代码能够讲述有关该域的整个故事。BDD 借用了业务语言的思想,并通过工具支持使其更加字面化。最后,持续交付表明,如果我们决定遵循推荐的实践,几年前看起来很荒谬的想法(以非事件方式每天交付几次)实际上是可能的,甚至是可取的。

With TDD, the tests are first considered as specifications. With DDD, we identify the code and the modeling of the business domain, breaking with the tradition of models kept separately from the code. One consequence is that we expect the code to tell the whole story about the domain. BDD borrowed the idea of the business language and made it more literal, with tool support. Finally, continuous delivery is showing that an idea that looked ridiculous a few years ago (delivering several times a day in a non-event fashion) is actually possible and even desirable if we decide to follow the recommended practices.

正在发生的另一件有趣的事情是由于时间的影响:尽管像文学编程或 HyperCard 这样的旧思想没有成为主流,但它们仍然缓慢而悄然地产生影响,特别是在 F# 和 Clojure 等较新的编程语言社区中,它们带来了一些旧观念被推到前台。

Another interesting thing that is happening is due to the effect of time: Even though old ideas like literate programing or HyperCard did not become mainstream, they remained slowly and quietly influential, especially in more recent programming languages communities such as F# and Clojure, which bring some of the old ideas to the foreground.

现在,我们终于可以期待一种有用、始终最新、成本低廉且创建起来充满乐趣的文档方法。我们承认传统记录方法存在的所有问题,并且我们也看到有必要予以满足。本书探讨了其他方法并提供了指导,以更有效地满足需求。但首先,让我们探讨一下文档到底是什么。

Now at last we can expect an approach to documentation that is useful, always up-to-date, low cost, and fun to create. We acknowledge all the problems of the traditional approach to documentation, and we also see that there is a need to be fulfilled. This book explores and offers guidance on other approaches to meet the needs in more efficient ways. But first, let’s explore what documentation really is.

文档是关于知识的

Documentation Is About Knowledge

软件开发都是关于知识和基于这些知识的决策,这反过来又创造了额外的知识。给定的问题、做出的决定、做出这种决定的原因、导致该决定的事实以及考虑的替代方案都是知识。

Software development is all about knowledge and decision-making based on that knowledge, which in turn creates additional knowledge. The given problem, the decision that was made, the reason it was made that way, the facts that led to that decision, and the considered alternatives are all knowledge.

您可能从未这样想过,但用编程语言输入的每条指令都是一个决定。决定有大有小,但无论大小,都是决定。在软件开发中,设计阶段之后没有昂贵的构建阶段:构建(运行编译器)非常便宜,因此只有一个昂贵的(有时是永久性的)设计阶段。

You may not have ever thought about it this way, but each instruction typed in a programming language is a decision. There are big and small decisions, but no matter the size, they are all decisions. In software development, there is no expensive construction phase following a design phase: The construction (running the compiler) is so cheap that there’s only an expensive—and sometimes everlasting—design phase.

软件设计可以持续很长时间。它可以持续足够长的时间,足以忘记之前做出的决定及其背景。它可以持续足够长的时间,让人们离开并带走他们的知识,也让新人加入但缺乏知识。知识是软件开发等设计活动的核心。

Software design can last a long time. It can last long enough to forget about previous decisions made, as well as their contexts. It can last long enough for people to leave, taking with them their knowledge, and for new people to join, lacking knowledge. Knowledge is central to a design activity like software development.

大多数时候,由于许多充分的原因,这种设计活动是一项涉及多个人的团队努力。一起工作意味着一起做出决定或根据其他人的知识做出决定。

Most of the time this design activity is, for many good reasons, a team effort involving more than one person. Working together means making decisions together or making decisions based on someone else’s knowledge.

软件开发的独特之处在于,设计不仅涉及人,还涉及机器。计算机是图像的一部分,许多决策只是交给计算机来执行。这通常是通过称为源代码的文档来完成的。使用编程语言等形式语言,我们以计算机可以理解的形式将知识和决策传递给计算机。

Something unique with software development is that the design involves not only people but also machines. Computers are part of the picture, and many of the decisions made are simply given to the computer to execute. This is usually done through documents called source code. Using a formal language like a programming language, we pass knowledge and decisions to a computer in a form it can understand.

不过,让计算机理解源代码并不是困难的部分。即使是缺乏经验的开发人员通常也能成功。最困难的部分是让其他人了解已经完成的工作,以便他们可以更好更快地完成工作。

Having a computer understand source code is not the hard part, though. Even inexperienced developers usually manage to succeed at that. The hardest part is for other people to understand what has been done so that they can then do better and faster work.

野心越大,就越需要更多的文档来实现超出我们头脑范围的知识管理累积过程。当我们的大脑和记忆力不够时,我们需要书写、印刷和软件等技术的帮助来帮助记忆和组织更大的知识集。

The greater the ambition, the more documentation becomes necessary to enable a cumulative process of knowledge management that scales beyond what fits in our heads. When our brains and memories are not enough, we need assistance from technologies such as writing, printing, and software to help remember and organize larger sets of knowledge.

知识的起源

The Origination of Knowledge

知识从哪里来?知识主要来自对话。我们通过与其他人的对话发展了很多知识。这种情况发生在集体工作期间,例如结对编程,或在会议期间,或在咖啡机旁,通过电话,或通过公司聊天或电子邮件。对话的示例包括 BDD 规范研讨会和敏捷领域的三个朋友。

Where does knowledge come from? Knowledge primarily comes from conversations. We develop a lot of knowledge through conversations with other people. This happen during collective work such as pair programming, or during meetings, or at the coffee machine, on the phone, or via a company chat or emails. Examples of conversations include BDD specification workshops and the three amigos in agile.

然而,作为软件开发人员,我们也与机器进行对话,我们将这些称为实验。我们通过使用某种编程语言的代码向机器告诉一些信息,机器运行它并告诉我们一些回报:测试失败或变绿,UI 按预期反应,或者结果不是我们想要的,其中如果我们学到新东西。实验示例包括 TDD、新兴设计和精益创业实验。

However, as software developers, we have conversations with machines, too, and we call these experiments. We tell something to a machine by using code in some programming language, and the machine runs it and tells us something in return: The test fails or goes green, the UI reacts as expected, or the result is not what we wanted, in which case we learn something new. Examples of experiments include TDD, emerging design, and Lean Startup experiments.

知识也来自对上下文的观察。在公司里,只要呆在公司,关注其他人的谈话、行为和情绪,你就能学到很多东西。观察的例子包括领域沉浸、痴迷墙、信息辐射器和精益创业“走出大楼”观察。

Knowledge also comes from observation of the context. In a company, you learn a lot by just being there, paying attention to other people’s conversations, behavior, and emotions. Examples of observation include domain immersion, obsession walls, information radiators, and Lean Startup “Get out of the building” observation.

知识来自于与人的对话以及在可观察的环境中对机器进行的实验。

Knowledge comes from conversations with people and experiments with machines in an observable context.

知识如何演化?

How Does Knowledge Evolve?

有些知识可以稳定数年,而另一些知识则在数月甚至数小时内频繁变化。

Some knowledge can be stable for years, whereas other knowledge changes frequently over months or even hours.

任何形式的文档都必须考虑维护成本并使其尽可能接近于零。为了获得稳定的知识,传统的文档方法是有效的。但随着知识的频繁变化,编写文本并在每次更改后更新它是不可行的。

Any form of documentation has to consider the cost of maintenance and make it as close to zero as possible. For stable knowledge, traditional methods of documentation work. But with frequently changing knowledge, writing text and updating it after every change is just not an option.

软件行业加速的结果是我们希望能够非常快速地发展软件。速度如此之快,以至于不可能花时间编写一页又一页的文档,但我们想要文档的所有好处。

The effect of acceleration in the software industry is that we want to be in a position to evolve the software very quickly. The speed is such that it’s impossible to spend time writing pages and pages of documentation, and yet we want all the benefits of documentation.

为什么需要知识

Why Knowledge Is Necessary

在创建软件时,我们会在学习过程中遇到很多问题、做出决定和调整:

When creating software, we go through a lot of questions, decisions, and adjustments as we learn:

  • 我们要解决什么问题?从现在开始大家都应该知道了。

  • What problem are we trying to solve? Everyone should know it from now on.

  • 我们真正想解决什么问题?(当我们意识到我们最初弄错了时,我们会尝试回答这个问题。)

  • What problem are we really trying to solve? (We try to answer this when we realize we got it wrong initially.)

  • 我们一直混淆tradedeal,但我们最终意识到它们不是同义词。我们不应该再次混淆他们。

  • We’ve been confusing trade and deal, but we eventually realized that they are not synonyms. We should not confuse them again.

  • 我们尝试了这个新的数据库,但它不符合我们的需求,原因有三个。只要我们的需求保持不变,就无需稍后再试。

  • We tried this new DB, and it doesn’t match our needs—for three reasons. No need to try again later as long as our needs remain the same.

  • 我们决定将购物车模块和支付模块解耦,因为我们注意到其中一个模块的更改与另一个模块的更改无关。我们不应该再次将它们耦合起来。

  • We decided to decouple the shopping cart module and the payment module because we noticed that the changes to one had nothing to do with changes to the other. We should not couple them again.

  • 我们偶然发现这个功能没什么用,所以我们计划下个月删除该代码。但我们很可能会忘记我们的基本原理,如果代码仍然存在,它将永远是一个谜。

  • We found out by chance that this feature is useless, so we plan to delete the code next month. But we are likely to forget our rationale, and if the code remains, it will be a mystery forever.

使用现有的软件,当我们错过以前开发的知识时,我们最终会重做已经完成的事情,因为我们不知道它已经存在。我们最终还会将某个功能放入不相关的组件中,因为我们不知道它应该放在哪里,这使得软件变得臃肿。或者有关该功能的代码变得分散在各个组件中。

With existing software, when we miss the knowledge developed before, we end up redoing what’s already done because we don’t know it’s there already. We also end up putting a feature in an unrelated component because we don’t know where it should be, and this makes the software bloated. Or the code about the feature becomes fragmented across various components.

如果我们有足够的知识来回答以下日常问题就好了:

If only we had the knowledge available to answer everyday questions like the following:

  • 我在哪里可以安全地解决该问题?

  • Where can I fix that issue safely?

  • 我应该在哪里添加此增强功能?

  • Where should I add this enhancement?

  • 原作者会在哪里添加此增强功能?

  • Where would the original authors add this enhancement?

  • 删除这行看起来无用的代码是否安全?

  • Is it safe to delete this line of code that looks useless?

  • 我想更改方法签名,但是这样做会产生什么影响?

  • I’d like to change a method signature, but what impacts will result if I do?

  • 我真的需要对代码进行逆向工程才能理解它是如何工作的吗?

  • Do I really have to reverse engineer the code just to understand how it works?

  • 每次业务分析师需要了解当前的业务规则时,我真的需要花时间阅读源代码吗?

  • Do I really have to spend time reading the source code each time the business analysts need to know about the current business rules?

  • 当客户请求某项功能时,我们如何知道该功能是否已受支持以及是否需要开发?

  • When a customer asks for a feature, how do we know if it’s already supported if it needs to be developed?

  • 我们感觉我们改进代码的方式是最好的,但是如果我们对它的工作原理缺乏完整的理解怎么办?

  • We have the feeling that the way we evolve the code is the best possible, but what if we lack a complete understanding of how it works?

  • 我们如何轻松地找到处理特定功能的代码部分?

  • How do we easily find the part of the code that deals with a particular feature?

缺乏知识表现为两种成本:

Lack of knowledge manifests as two costs:

  • 浪费时间:这些时间本来可以更好地投入到改进其他方面。

  • Wasted time: That time could have been better invested in improving something else.

  • 次优决策:从长远来看,其他决策可能更相关或更便宜。

  • Suboptimal decisions: Other decisions could have been more relevant, or cheaper in the long term.

随着时间的推移,这两项费用会不断增加:花在寻找缺失知识上的时间就是没有花在做出更好决策上的时间。反过来,次优的决策会让我们的生活逐渐变得更加悲惨,直到我们别无选择,只能决定软件不再可维护并重新开始。

These two expenses compound over time: The time spent finding missing knowledge is time not spent on making better decisions. In turn, suboptimal decisions compound to make our life progressively more miserable, until we have no choice but to decide that the software is no longer maintainable and start again.

能够访问对执行开发任务有用的知识听起来是个好主意。

It sounds like a good idea to be able to access the knowledge that is useful to perform the development tasks.

软件编程作为理论构建和传递

Software Programming as Theory Building and Passing

1985 年,Peter Naur 的著名论文《编程作为理论构建》完美地揭示了编程作为一项集体努力的真相:他说,与其说是告诉计算机要做什么,不如说是与其他开发者分享编程的理论。这个世界(想想“心智模型”)是通过学习、实验、对话和深刻反思耐心阐述的。用他自己的话说:

In 1985, Peter Naur’s famous paper “Programming as Theory Building” perfectly revealed the truth about programming as a collective endeavor: He said that it’s not so much about telling the computer what to do as it is about sharing with other developers the theory of the world (think “mental model”) that has been patiently elaborated by learning, experiment, conversations, and deep reflections. In his own words:

正确的编程应该被视为一种活动,程序员可以通过这种活动形成或实现对当前问题的某种洞察力、理论。这一建议与似乎更常见的概念形成鲜明对比,即编程应被视为程序和某些其他文本的产生。5

Programming properly should be regarded as an activity by which the programmers form or achieve a certain kind of insight, a theory, of the matters at hand. This suggestion is in contrast to what appears to be a more common notion, that programming should be regarded as a production of a program and certain other texts.5

5. Peter Naur,“编程作为理论构建”,微处理和微编程,第 15 卷,第 5 期,1985 年,第 253-261 页。

5.Peter Naur, “Programming as Theory Building,” Microprocessing and Microprogramming, Volume 15, Issue 5, 1985, pp. 253–261.

问题是大部分理论都是默认的。该代码仅代表冰山一角。它更多的是开发者心中理论的结果,而不是理论本身的表现。在 Peter Naur 看来,这一理论涵盖了三个主要知识领域:

The problem is that most of the theory is tacit. The code only represents the tip of the iceberg. It’s more a consequence of the theory in the mind of the developers than a representation of the theory itself. In Peter Naur’s view, this theory encompasses three main areas of knowledge:

  • 代码与其所代表的世界之间的映射:掌握程序理论的程序员可以解释解决方案如何与其帮助处理的世界事务相关。

  • The mapping between code and the world it represents: The programmer who has the theory of the program can explain how the solution relates to the affairs of the world that it helps to handle.

  • 程序的原理:掌握了程序理论的程序员能够解释为什么程序的每一部分是这样的;换句话说,程序员能够以某种理由支持实际的程序文本。

  • The rationale of the program: The programmer who has the theory of the program can explain why each part of the program is what it is; in other words, the programmer is able to support the actual program text with a justification of some sort.

  • 程序扩展或进化的潜力:掌握程序理论的程序员能够建设性地响应任何修改程序的需求,以便以新的方式支持世界事务。

  • The potential of extension or evolution of the program: The programmer who has the theory of the program is able to respond constructively to any demand for modification of the program in order to support the affairs of the world in a new manner.

随着时间的推移,我们学到了许多使人们能够相互传递理论的技术。简洁的代码和 Eric Evans 的领域驱动设计鼓励程序员找到将头脑中的理论更字面地表达到代码中的方法。例如,DDD的无所不在的语言弥合了世界语言和代码语言之间的差距,帮助解决映射问题。我希望未来的编程语言能够认识到不仅需要表示代码的行为,还需要表示程序员更大的心理模型,而代码就是该模型的结果。

Over time, we’ve learned a number of techniques that enable people to pass theories among themselves. Clean code and Eric Evans’s domain-driven design encourage programmers to find ways of expressing the theory in their heads more literally into the code. For example, DDD’s ubiquitous language bridges the gap between the language of the world and the language of the code, helping solve the mapping problem. I hope future programming languages will recognize the need to represent not only the behavior of the code but also the bigger mental model of the programmers, of which the code is a consequence.

模式和模式语言也会浮现在脑海中,作为包装理论块的字面尝试。我们知道的模式越多,我们就越能对隐性理论进行编码,使其变得明确并可以在更广泛的范围内转移。模式在对其力量的描述中体现了选择它们的基本原理的关键要素,并且它们有时暗示扩展应该如何发生。他们可能会暗示该计划的潜力;例如,策略模式旨在通过添加新策略来扩展。

Patterns and pattern languages also come to mind, as literal attempts to package nuggets of theories. The more patterns we know, the more we can encode the tacit theory, making it explicit and transferable to a wider extent. Patterns embody in the description of their forces the key elements of the rationale in choosing them, and they sometimes hint at how extension should happen. They might hint at the potential of the program; for example, a strategy pattern is meant to be extended by adding new strategies.

但随着我们在理解的编纂方面取得进展,我们也应对了更雄心勃勃的挑战,因此我们的挫败感仍然是一样的。我相信Naur 1985年的那句话在未来几十年仍然有效:

But as we progress in the codification of our understanding, we also tackle more ambitious challenges, so our frustration remains the same. I  believe Naur’s sentence from 1985 will still hold in the next decades:

对于新程序员来说,要掌握现有的程序理论,他或她有机会熟悉程序文本和其他文档是不够的。6

For a new programmer to come to possess an existing theory of a program it is insufficient that he or she has the opportunity to become familiar with the program text and other documentation.6

6. Peter Naur,“编程作为理论构建”,《微处理和微编程》,第 15 卷,第 5 期,1985 年,第 253-261 页。

6.Peter Naur, “Programming as Theory Building,” Microprocessing and Microprogramming, Volume 15, Issue 5, 1985, pp. 253–261.

我们永远无法完全解决知识转移问题,但我们可以接受它作为事实并学会接受它。该理论作为程序员头脑中的心理模型,永远无法与那些没有参与构建该理论的思维过程的人完全共享。

We’ll never completely solve that knowledge transfer problem, but we can accept it as a fact and learn to live with it. The theory as a mental model in programmers’ heads can never be fully shared with those who weren’t part of the thought process that led to building it.

结论似乎是不可避免的:至少在某些类型的大型程序中,持续的适应、修改和纠错依赖于一群彼此密切且持续联系的程序员所拥有的某种知识。

The conclusion seems inescapable: At least in certain kinds of large programs, the continued adaption, modification, and correction of errors is dependent on a certain kind of knowledge possessed by a group of programmers who are closely and continuously connected to each other.

值得注意的是,定期集体工作的固定团队不会因为理论传递问题而受到太大影响。

It’s worth noting that permanent teams that regularly work collectively don’t suffer too much from this issue of theory passing.

文档是关于知识的传递

Documentation Is About Transferring Knowledge

文档这个词经常让人想起很多含义:书面文档、Microsoft Word 或 PowerPoint 文档、基于公司模板的文档、印刷文档、网站或 wiki 上的大而重且无聊的文本等等。然而,所有这些内涵都将我们锚定在过去的实践中,并且排除了许多更新、更有效的实践。

The word documentation often brings a lot of connotations to mind: written documents, Microsoft Word or PowerPoint documents, documents based on company templates, printed documents, big, heavy and boring text on a website or on a wiki, and so on. However, all these connotations anchor us to practices of the past, and they exclude a lot of newer and more efficient practices.

出于本书的目的,我们将采用更广泛的文档定义:

For the purposes of this book, we’ll adopt a much broader definition of documentation:

将有价值的知识传递给现在和未来的其他人的过程。

The process of transferring valuable knowledge to other people now and also to people in the future.

文档有一个后勤方面的内容。它涉及在人与人之间在空间中转移知识,也涉及随着时间的推移转移知识,技术人员将其称为持久性存储。总的来说,我们对文档的定义就像货物的运输和仓储,其中货物就是知识。

There’s a logistic aspect to documentation. It’s about transferring knowledge in space between people and also about transferring it over time, which technical people call persistence or storage. Overall, our definition of documentation looks like shipment and warehousing of goods, where the goods are knowledge.

人与人之间的知识传递实际上就是大脑之间的知识传递(见图1.7)。从一个大脑到另一个大脑,这是一个传播或扩散的问题(例如,到达更多的受众)。从现在的大脑到后来的大脑,这是关于坚持知识的问题,这是一个记忆的问题。

Transferring knowledge between people is actually transferring knowledge between brains (see Figure 1.7). From one brain to other brains, it’s a matter of transmission, or diffusion (for example, to reach a larger audience). From brains now to brains later, it’s about persisting the knowledge, and it’s a matter of memory.

一个图说明了文档是关于转移和存储知识的。

图1.7 文档是关于知识的传输和存储

Figure 1.7 Documentation is about transferring and storing knowledge

你可知道?

Did you know?

开发半衰期为 3.1 年,而代码半衰期为 13 年。7文档必须帮助解决这种不匹配问题。

The development tenure half-life is 3.1 years, whereas the code half-life is 13 years.7 Documentation has to help with this mismatch.

7. Rob Smallshire,六十北博客, http://sixty-north.com/blog/predictive-models-of-development-teams-and-the-systems-they-build

7.Rob Smallshire, Sixty North blog, http://sixty-north.com/blog/predictive-models-of-development-teams-and-the-systems-they-build

将知识从技术人员的大脑转移到非技术人员的大脑就是使知识易于获取。使知识易于获取的另一个例子是使其可有效地搜索

Transferring knowledge from the brain of a technical person to the brains of nontechnical people is a matter of making the knowledge accessible. Another case of making knowledge accessible is to make it efficiently searchable.

还有其他情况,例如出于合规性原因需要将知识放入特定的文档格式中 - 因为您必须这样做。

And there are other situations, such as needing to put knowledge into a specific document format for compliance reasons—because you just have to.

专注于重要的事情

Focusing on What Matters

作为传递有价值知识的一种手段,文档可以采取多种形式:书面文档、面对面的对话、代码、社交工具上的活动,或者在没有必要时什么都不写。

As a means of transferring valuable knowledge, documentation can take many forms: written documents, face-to-face conversations, code, activity on social tools, or nothing at all when it’s not necessary.

通过文档的这个定义,我们可以表达一些重要的原则:

With this definition of documentation, we can express some important principles:

  • 长期感兴趣的知识值得记录下来。

  • Knowledge that is of interest for a long period of time deserves to be documented.

  • 许多人感兴趣的知识值得记录下来。

  • Knowledge that is of interest to a large number of people deserves to be documented.

  • 有价值关键的知识可能也需要记录下来。

  • Knowledge that is valuable or critical may also need to be documented.

另一方面,您不需要关心这些情况下不存在的知识文档。花时间或精力在上面都是浪费。

On the other hand, you don’t need to care about documentation of knowledge that isn’t in any of these cases. Spending time or effort on it would be a waste.

所考虑的知识的价值很重要。如果知识在足够长的时间内对足够多的人来说不够有价值,则无需付出努力。如果一项知识已经众所周知或仅对一个人有用,或者直到一天结束时才感兴趣,那么就没有必要转移或存储它。

The value of the considered knowledge matters. There’s no need to make the effort to transfer knowledge that’s not valuable enough for enough people over a long enough period of time. If a piece of knowledge is already well known or is useful for only one person, or if it’s only of interest until the end of the day, then there’s no need to transfer or store it.

默认是不

The Default Is Don’t

除非有令人信服的理由,否则在记录知识方面做出任何具体努力是没有意义的。否则就是浪费。不要因为没有记录不需要记录的东西而感到难过。

There is no point in making any specific effort at documenting knowledge unless there’s a compelling reason to do it; otherwise, it’s a waste. Don’t feel bad about not documenting something that doesn’t need to be documented.

在从知识传播和保存的角度重新考虑文档到底是什么,以及如何管理文档的一些早期后果之后,现在是时候介绍活文档的中心思想及其核心原则了。

Having reconsidered what documentation really is, in terms of knowledge transmission and preservation, and some early consequences in how it should be managed, it is now time to introduce the central idea of living documentation and its core principles.

活文档的核心原则

Core Principles of Living Documentation

“活文档”一词在Gojko Adzic 所著的《实例化规范》一书中首次流行起来。Adzic 描述了团队执行 BDD 的一个主要好处:他们为规范和测试创建的场景作为业务行为的文档也非常有用。由于测试自动化,只要测试全部通过,该文档就始终是最新的。

The term living documentation first became popular in the book Specification by Example by Gojko Adzic. Adzic described a key benefit of teams doing BDD: Their scenarios created for specifications and testing were also very useful as documentation of the business behaviors. Thanks to the test automation, this documentation was always up-to-date, as long as the tests were all passing.

对于软件开发项目的各个方面,都可以从活文档中获得相同的好处:当然,还有业务领域、项目愿景和业务驱动因素、设计和架构、遗留策略、编码指南、部署和基础设施。

It is possible to get the same benefits of living documentation for all aspects of a software development project: business behaviors, of course, but also business domains, project vision and business drivers, design and architecture, legacy strategies, coding guidelines, deployment, and infrastructure.

活文档涉及四个原则(见图1.8):

Living documentation involves a set of four principles (see Figure 1.8):

  • 可靠:实时文档在任何时间点都是准确的并且与正在交付的软件同步。

  • Reliable: Living documentation is accurate and in sync with the software being delivered, at any point in time.

  • 省力:活文档可以最大限度地减少文档工作量,即使在发生更改、删除或添加时也是如此。它只需要最少的额外努力——而且只需要一次。

  • Low effort: Living documentation minimizes the amount of work to be done on documentation, even in case of changes, deletions, or additions. It requires only minimal additional effort—and only once.

  • 协作:活文档促进每个相关人员之间的对话和知识共享。

  • Collaborative: Living documentation promotes conversations and knowledge sharing between everyone involved.

  • 富有洞察力:通过引起人们对工作各个方面的关注,活文档提供了反馈的机会并鼓励更深入的思考。它有助于反思正在进行的工作并有助于做出更好的决策。

  • Insightful: By drawing attention to each aspect of the work, living documentation offers opportunities for feedback and encourages deeper thinking. It helps reflect on the ongoing work and helps in making better decisions.

图中显示了活文档的原则,例如富有洞察力、省力、协作和可靠。

图1.8 活文档的原则

Figure 1.8 Principles of living documentation

活文档还为开发人员和其他团队成员带来了乐趣。他们可以专注于做得更好,同时他们还能从这项工作中获得活生生的文档。

Living documentation also brings the fun back for developers and other team members. They can focus on doing a better job, and at the same time they get the living documentation out of this work.

以下各节简要描述了活文档的四个核心原则,这些原则将共同作为指导,以从该方法中释放最大的好处。这些重要的想法将在本章的其余部分和接下来的三章中详细阐述。

The following sections briefly describe the four core principles of living documentation that together will act as a guidance to unlock the most benefits from the approach. These important ideas are then elaborated on in the rest of the chapter and in the next three chapters.

可靠的

Reliable

为了有用,文档必须值得信赖;换句话说,它必须是100%可靠的。由于人类从来都不是那么可靠,因此我们需要纪律和工具来帮助提高可靠性。

To be useful, documentation must be trustworthy; in other words, it must be 100% reliable. Since humans are never that reliable, we need discipline and tools to help with reliability.

为了获得可靠的文档,我们依靠以下想法:

To achieve reliable documentation, we rely on the following ideas:

  • 利用可用的知识:大多数知识已经存在于项目的工件中,只需要为了文档目的而对其进行利用、增强和管理。

  • Exploiting available knowledge: Most of the knowledge is already present in the artifacts of the project, it just needs to be exploited, augmented, and curated for documentation purposes.

  • 准确性机制:需要一个准确性机制来保证知识始终保持同步。

  • Accuracy mechanism: An accuracy mechanism is needed to ensure that the knowledge is always kept in sync.

省力

Low Effort

活生生的文档必须是省力的,才能在不断变化的环境中可行且可持续;您可以通过以下想法实现这一目标:

A living documentation must be low effort to be feasible and sustainable on ever-changing environments; you can achieve that thanks to the following ideas:

  • 简单性:如果没有什么需要声明的,那么文档是最好的,但它是显而易见的。

  • Simplicity: Documentation is best if there is nothing to declare, but it’s just obvious.

  • 标准优于自定义解决方案:标准应该是已知的,如果情况并非如此,则只需在外部参考(例如,您最喜欢的书籍、作者或维基百科)中引用标准就足够了。

  • Standard over custom solutions: Standards are supposed to be known, and if that’s not the case, it is enough to just refer to a standard in an external reference (for example, your favorite books, authors, or Wikipedia).

  • 常青内容:总有一些内容不会改变或很少改变,而且这种材料的维护成本并不高。

  • Evergreen content: There is always stuff that does not change or that changes very infrequently, and this material does not cost much to maintain.

  • 防重构知识:有些事情发生变化时不需要人为的努力。这可能是因为重构工具会自动传播链接的更改,或者因为某些事物固有的知识与该事物本身并置,并且它会随该事物而变化和移动。

  • Refactoring-proof knowledge: Some things don’t require human effort when there is a change. This can be because of refactoring tools that automatically propagate linked changes, or because knowledge intrinsic to something is collocated with the thing itself, and it changes and moves with that thing.

  • 内部文档:关于事物的附加知识最好位于事物本身,或尽可能接近。

  • Internal documentation: Additional knowledge about a thing is best located on the thing itself, or as close as possible.

协作性

Collaborative

活文档必须通过以下偏好进行协作:

A living documentation must be collaborative through the following preferences:

  • 优先采用对话而非正式文档:没有什么比交互式、面对面的对话更能有效地交流知识。不要因为没有记录每次讨论而感到难过。尽管我通常喜欢对话,但有些知识在很长一段时间内或对许多人来说一直是有用的。重要的是要注意随着时间的推移,思想沉淀的过程,以确定哪些知识值得以持久的形式记录下来。

  • Favor conversations over formal documentation: Nothing beats interactive, face-to-face conversations for exchanging knowledge efficiently. Don’t feel bad about not keeping a record of every discussion. Even though I usually favor conversations, there is knowledge that keeps on being useful repeatedly over a long period of time or for many people. It is important to pay attention to the process of ideas sedimentation over time to decide what knowledge is worth the effort of recording in a persistent form.

  • 可访问的知识:在活文档方法中,知识通常在源代码控制系统的技术工件中声明。这使得非技术人员很难访问它。因此,您应该提供工具,使所有受众无需任何手动操作即可获取这些知识。

  • Accessible knowledge: In a living documentation approach, knowledge is often declared within technical artifacts in a source control system. This makes it difficult for nontechnical people to access it. Therefore, you should provide tools to make this knowledge accessible to all audiences without any manual effort.

  • 集体所有权:并不是因为所有知识都在源代码控制系统中,开发人员才拥有它。开发人员不拥有文档;他们只承担处理它的技术责任。

  • Collective ownership: It’s not because all the knowledge is in the source control system that developers own it. Developers don’t own the documentation; they just own the technical responsibility to deal with it.

富有洞察力

Insightful

上述原则很有用,但要充分发挥活文档的潜力,它必须具有洞察力:

The above-mentioned principles are useful, but to realize the full potential of a living documentation, it must be insightful:

  • 深思熟虑的决策:如果您不清楚自己在做什么,那么当您准备进行实时记录时,它会立即显示出来。这种反馈鼓励您澄清您的决定,以便您所做的事情变得容易解释。通过鼓励更加深思熟虑的决策,这通常会提高工作质量。

  • Deliberate decision making: If you don’t know clearly what you’re doing, it shows immediately when you’re about to do living documentation. This kind of feedback encourages you to clarify your decisions so that what you do becomes easy to explain. By encouraging more deliberate decision-making, this will often raise the quality of the work.

  • 嵌入式学习:您希望编写非常好的代码和其他技术工件,以便您的同事只需通过使用系统、通过交互来学习即可学习设计、业务领域和其他所有内容。

  • Embedded learning: You want to write code and other technical artifacts that are so good that your workmates can learn the design, the business domain, and everything else just by working with the system, learning through their interactions.

  • 现实检查:实时文档有助于揭示系统的实际状态(例如,“我没想到实现会那么混乱”,如“我以为我刮得正确,但镜子告诉我情况并非如此。”)。再次,这可以通过接受现实的本来面目而不是你想要的样子来促进改进。

  • Reality check: Living documentation helps reveal the actual state of the system (for example, “I did not expect the implementation to be that messy,” as in “I thought I was shaved correctly, but the mirror tells otherwise.”). This, again, can foster improvements by accepting reality for what it is, as opposed to what you’d like it to be.

以下各节将更详细地描述这些原则,而接下来的章节将扩展至相关模式和实践,以实施成功的活文档方法。但在此之前,令人惊讶的是,我们可以在蚂蚁和其他社会性昆虫协作和交换知识的方式中找到活体记录的重要灵感。

The following sections describe these principles more in details, while the next chapters expand into the related patterns and practices to implement a successful living documentation approach. But before that, an important inspiration for living documentation can be found, surprisingly, in the way ants and other social insects collaborate and exchange knowledge.

蚂蚁如何交换知识:Stigmergy

How Ants Exchange Knowledge: Stigmergy

Michael Feather (@mfeathers) 最近分享了 Ted Lewis 在网上发表的一篇精彩文章的链接,他介绍了与我们作为一个团队在软件领域工作相关的污蔑概念:

Michael Feather (@mfeathers) recently shared a link to a fantastic article online by Ted Lewis, who introduces the concept of stigmergy in relation to our work in software as a team:

法国昆虫学家皮埃尔-保罗·格拉塞(Pierre-Paul Grassé)描述了一种昆虫协调机制,他称之为“stigmergy”——演员表演的作品会刺激同一演员或其他演员的后续作品。也就是说,建筑物、代码库、高速公路或其他物理建筑的状态决定了下一步需要做什么,而无需中央规划或专制统治。参与者——昆虫或程序员——根据已经完成的事情知道下一步该做什么。这种扩展他人工作的直觉冲动成为现代软件开发的组织原则。

The French entomologist Pierre-Paul Grassé described a mechanism of insect coordination he called “stigmergy”—work performed by an actor stimulates subsequent work by the same or other actors. That is, the state of a building, code base, highway, or other physical construction determines what needs to be done next without central planning or autocratic rule. The actors—insects or programmers—know what to do next, based on what has already been done. This intuitive urge to extend the work of others becomes the organizing principle of modern software development.

蚂蚁使用一种特殊类型的化学标记——信息素——来突出其活动的结果。8

Ants use a special type of chemical marker—pheromones—to highlight the results of their activity.8

8. Ted Lewis,Ubiquity 博客, http://ubiquity.acm.org/blog/why-cant-programmers-be-more-like-ants-ora-lesson-in-stigmergy

8.Ted Lewis, Ubiquity blog, http://ubiquity.acm.org/blog/why-cant-programmers-be-more-like-ants-ora-lesson-in-stigmergy

同样,程序员通过电子邮件、GitHub 问题和各种增强代码的文档来制造自己的标记。正如刘易斯总结的那样:

Similarly, programmers manufacture their own markers through emails, GitHub issues, and all kinds of documentation that augments code. As Lewis concludes:

现代软件开发的本质是代码库中嵌入的智能和标记。通过更可靠地将程序员的注意力集中在需要完成的工作中最相关的方面,标记使污名化更加有效。9

The essence of modern software development is stigmergic intellect and markers embedded within the code base. Markers make stigmergy more efficient, by more reliably focusing a programmer’s attention on the most relevant aspects of the work that needs to be done.9

9. Ted Lewis,Ubiquity 博客, http://ubiquity.acm.org/blog/why-cant-programmers-be-more-like-ants-ora-lesson-in-stigmergy

9.Ted Lewis, Ubiquity blog, http://ubiquity.acm.org/blog/why-cant-programmers-be-more-like-ants-ora-lesson-in-stigmergy

Stigmergy 已经成为我们在开发软件时在人与机器之间交换知识的重要方式。活体记录的一个关键思想是承认这种耻辱效应的存在,并找到将其发挥到极致的方法。首先,像蚂蚁一样,从您所在的系统中获取大部分知识。

Stigmergy is already the prominent way we exchange knowledge between the people and the machines involved when doing software. One key idea of living documentation is to acknowledge that this stigmergic effect is there, and to find ways to push it to the max. This starts by getting most of the knowledge out of the system you’re in, like ants do.

大多数知识已经存在

Most Knowledge Is Already There

当某条知识已经记录在系统本身中时,就没有必要记录它。

There is no need to record a piece of knowledge when it is already recorded in the system itself.

每个有趣的项目都是一次产生特定知识的学习旅程。我们通常期望文档能够为我们提供所需的具体知识,但是有趣的是,所有这些知识都已经存在:在源代码中,在配置文件中,在测试中,在运行时应用程序的行为中,在所涉及的各种工具的内存中,当然,在大脑中所有致力于此的人。

Every interesting project is a learning journey that produces specific knowledge. We usually expect documentation to give us the specific knowledge we need, but the funny thing is that all this knowledge is already there: in the source code, in the configuration files, in the tests, in the behavior of the application at runtime, in memory of the various tools involved, and, of course, in the brains of all the people working on it.

在软件项目中,大多数知识都以某种形式存在于工件中的某个位置。这类似于蚂蚁主要从巢穴本身学习如何进化它们的巢穴。

In a software project, most of the knowledge is present in some form somewhere in the artifacts. It is similar to ants learning how to evolve their nest mostly from the nest itself.

因此:承认大部分知识已经存在于系统本身中。需要时,确定它所在的位置并从那里利用它。

Therefore: Acknowledge that most of the knowledge is already in the system itself. When needed, identify where it is located and exploit it from there.

即使知识存在于某处,这并不意味着对此无能为力。现有的知识存在许多问题:

Even if the knowledge is there somewhere, this does not mean that there is nothing to do about it. There are a number of problems with the knowledge that’s already there:

  • 不可访问:非技术人员无法访问存储在源代码和其他工件中的知识。例如,非开发人员无法读取源代码。

  • Inaccessible: The knowledge stored in the source code and other artifacts is not accessible to nontechnical people. For example, source code is not readable by nondevelopers.

  • 过于丰富:项目工件中存储了大量知识,导致无法有效利用知识。例如,每行逻辑代码都编码知识,但对于给定的问题,只有一两行可能与答案相关。

  • Too abundant: Huge amounts of knowledge are stored in the project artifacts, which makes it impossible to use the knowledge efficiently. For example, each logical line of code encodes knowledge, but for a given question, only one or two lines may be relevant to the answer.

  • 碎片化:有些知识我们认为是一个整体,但实际上分布在项目工件的多个位置。例如,Java 中的类层次结构通常分布在多个文件中,每个文件对应一个子类,尽管我们倾向于将类层次结构视为一个整体。

  • Fragmented: There is knowledge that we think of as one single piece but that is in fact spread over multiple places in the project’s artifacts. For example, a class hierarchy in Java is usually spread over multiple files, one for each subclass, even though we tend to think about the class hierarchy as a whole.

  • 隐式:许多知识隐式地存在于现有的工件中。例如,可能有 99%,但缺少 1% 以便使其明确。例如,当您使用诸如组合之类的设计模式时,仅当您熟悉该模式时,该模式才在代码中可见。

  • Implicit: A lot of knowledge is present implicitly in the existing artifacts. It may be, for example, 99% there but missing the 1% to make it explicit. For example, when you use a design pattern like a composite, the pattern is visible in the code only if you’re familiar with the pattern.

  • 不可恢复:知识可能存在,但由于过于模糊而无法恢复。比如,业务逻辑是用代码来表达的,但是代码写得很烂,没有人能看懂。

  • Unrecoverable: It may be that the knowledge is there but that there is no way to recover it because it’s excessively obfuscated. For example, business logic is expressed in code, but the code is so bad that nobody can understand it.

  • 不成文:在最坏的情况下,知识只存在于人们的大脑中,系统中也只有其后果。例如,可能存在一般业务规则,但它可能已被编程为一系列特殊情况,因此一般规则没有在任何地方表达。

  • Unwritten: In the worst case, the knowledge is only in people’s brains, and only its consequences are there in the system. For example, there may a general business rule, but it may have been programmed as a series of special cases, so the general rule is not expressed anywhere.

内部文档

Internal Documentation

存储文档的最佳位置是记录在案的事物本身。

The best place to store documentation is on the documented thing itself.

您可能看过 Google 数据中心和巴黎蓬皮杜中心的图片(见图1.9)。它们的共同点是有很多颜色编码的管道,并在管道本身上印刷或铆接了额外的标签。蓬皮杜中心的空气管是蓝色的,水管是绿色的。这种颜色编码的逻辑扩展到了管道之外:电力运输是黄色的,有关人员移动的一切都是红色的,包括电梯和楼梯。

You’ve probably seen pictures of the Google datacenters and of the Centre Pompidou in Paris (see Figure 1.9). They have in common a lot of color-coded pipes, with additional labels printed or riveted on the pipes themselves. On the Pompidou Center, air pipes are blue and water pipes are green. This logic of color-coding expands beyond the pipes: Electricity transport is yellow, and everything about moving people is red, including the elevators and stairways.

下图显示了蓬皮杜中心大楼的颜色编码。

图 1.9 蓬皮杜中心大楼采用颜色编码

Figure 1.9 The Centre Pompidou building is color coded

这种逻辑在数据中心中也无处不在,甚至更多的文档直接打印在管道上。有标签可以识别管道,并且有箭头显示其中水流的方向。在现实世界中,这种颜色编码和临时标记通常是防火和消防所必需的:消防员的水管上铆有非常明显的标签,表明它们来自哪里。建筑物中的紧急出口在门上方非常明显。在飞机上,中央走廊上的明亮标志记录了要去的地方。在危机情况下,您没有时间寻找手册;你需要在最明显的地方找到答案:就在你所在的地方,就在事情本身。

This logic is also ubiquitous in datacenters, and even more documentation is printed directly on the pipes. There are labels to identify the pipes, and there are arrows to show the direction of the water flow in them. In the real world, such color-coding and ad hoc marking are often mandatory for fire prevention and firefighting: Water pipes for firefighters have very visible labels riveted on them, indicating where they come from. Emergency exits in buildings are made very visible above the doors. In airplanes, bright signs on the central corridors document where to go. In a situation of crisis, you don’t have time to look for a manual; you need the answer in the most obvious place: right where you are, on the thing itself.

内部文档与外部文档

Internal Versus External Documentation

持久性文档有两种形式:外部和内部。

Persistent documentation comes in two flavors: external and internal.

通过外部文档,知识以与项目所选择的实现技术无关的形式表达。这是传统形式的文档的情况,共享文件夹或 wiki 上有单独的 Microsoft Office 文档,并有自己的数据库。

With external documentation, knowledge is expressed in a form that has nothing to do with the chosen implementation technologies of the project. This is the case of the traditional forms of documentation, with separate Microsoft Office documents on shared folders or wikis with their own databases.

外部文档的一个优点是它可以采用对观众和作者来说最方便的任何格式和工具。缺点是要确保外部文档相对于产品的最新版本是最新的,即使不是不可能,也是极其困难的。外部文档也可能会丢失。

An advantage of external documentation is that it can take whatever format and tool is most convenient for the audience and for the writers. The drawback is that it’s extremely hard, if not impossible, to ensure that external documentation is up-to-date with respect to the latest version of the product. External documentation can also simply be lost.

相比之下,内部文档利用现有的实现技术直接表示知识。使用 Java 注释或语言标识符的命名约定来声明和解释设计决策是内部文档的一个很好的例子。

In contrast, internal documentation directly represents knowledge by using the existing implementation technology. Using Java annotations or naming conventions on the language identifiers to declare and explain design decisions is a good example of internal documentation.

内部文档的优点是它始终与产品的任何版本保持同步,因为它是源代码的一部分。内部文档不会丢失,因为它嵌入在源代码本身中。它也很容易获得,并且会引起任何处理代码的开发人员的注意,因为它就在他们的眼前。

The advantage of internal documentation is that it’s always up-to-date with any version of the product, as it’s part of its source code. Internal documentation cannot be lost because it’s embedded within the source code itself. It’s also readily available and comes to the attention of any developers working on the code just because it’s under their eyes.

内部文档还使您能够受益于出色的 IDE 的所有工具和所有优点,例如自动完成、即时搜索以及元素内部和元素之间的无缝导航。缺点是知识的表达仅限于语言内置的可能的扩展机制。例如,您无法通过有关每个依赖项的附加知识来扩展 Maven XML。另一个很大的缺点是,非开发人员不容易获取以内部文档形式表达的知识。然而,可以通过自动化机制来解决这一限制,该机制提取知识并将其转化为适当受众可以访问的文档。

Internal documentation also enables you to benefit from all the tools and all the goodness of your fantastic IDE, such as autocomplete, instant search, and seamless navigation within and between elements. The drawback is that your expression of the knowledge is limited to the possible extension mechanisms built in to the language. For example, there’s little you can do to extend the Maven XML with additional knowledge about each dependency. Another big drawback is that knowledge expressed as internal documentation is not readily accessible to nondevelopers. However, it is possible to work around that limitation with automated mechanisms that extract the knowledge and turn it into documents that are accessible to the right audience.

如果您熟悉Martin Fowler 和 Rebecca Parsons 所著的《领域特定语言》一书,您就会认识到内部与外部的类似概念领域特定语言 (DSL)。外部 DSL 独立于所选的实现技术。例如,正则表达式的语法与为项目选择的编程语言无关。相比之下,内部 DSL 使用常规选择的技术,例如 Java 编程语言,其方式使其看起来像是另一种伪装的语言。这种风格通常称为流畅风格,在模拟库中很常见。

If you’re familiar with the book Domain-Specific Languages by Martin Fowler and Rebecca Parsons, you’ll recognize the similar concept of internal versus external domain-specific languages (DSLs). An external DSL is independent from the chosen implementation technology. For example, the syntax of regular expressions has nothing to do with the programming language chosen for the project. In contrast, an internal DSL uses the regular chosen technology, such as the Java programming language, in a way that makes it look like another language in disguise. This style is often called a fluent style and is common in mocking libraries.

内部和外部文档示例

Examples of Internal and External Documentation

区分文档是内部文档还是外部文档并不总是那么容易,因为它有时与您的观点有关。Javadoc 是 Java 编程语言的标准部分,因此它是内部的。但从 Java 实现者的角度来看,它是嵌入在 Java 语法中的另一种语法,因此它是外部的。常规代码注释位于灰色中间区域。它们在形式上是语言的一部分,但除了自由文本之外不提供任何东西。你要靠你自己的写作天赋来写它们,编译器除了基于英语词典的默认拼写检查之外,不会帮助检查拼写错误。

It’s not always easy to tell whether documentation is internal or external, as it’s sometime relative to your perspective. Javadoc is a standard part of the Java programming language, so it’s internal. But from the Java implementors’ perspective, it’s another syntax embedded within the Java syntax, so it would be external. Regular code comments are in a gray middle area. They’re formally part of the language but do not provide anything more than free text. You’re on your own to write them with your writing talent, and the compiler will not help check for typos beside the default spell-checking based on the English dictionary.

从开发人员的角度来看,用于构建软件产品的每一项标准技术都可以被视为内部文档的宿主,包括以下内容:

From the perspective of the developer, every standard technology used to build a software product can be considered a host for internal documentation, including the following:

  • 用于业务可读规范和测试工具的功能文件

  • Feature files used for business-readable specifications and testing tools

  • 代码旁边的 Markdown 文件和图像具有命名约定,或者从代码或功能文件链接到

  • Markdown files and images next to the code with a naming convention or linked to from the code or feature files

  • 工具清单,包括依赖管理清单、自动化部署清单、基础设施描述清单等

  • Tools manifests, including the dependency management manifest, automated deployment manifest, infrastructure description manifest, and so on

每当我们在这些工件中添加文档时,我们都会受益于能够使用我们的标准工具集,并具有位于源代码控制中的优势,靠近相应的实现,以便它可以与之一起发展。

Whenever we add documentation within these artifacts, we benefit from being able to use our standard toolset and have the advantage of being in the source control, close to the corresponding implementation so that it can evolve together with it.

内部文档的潜在媒体示例包括:

Examples of potential media for internal documentation include the following:

  • 自记录代码和使用干净的代码实践,包括类和方法命名、使用组合方法和类型

  • Self-documenting code and use of clean code practices, including class and method naming, using composed methods and types

  • 为编程语言元素添加知识的注释

  • Annotations that add knowledge to elements of the programming language

  • 关于公共接口、类和主要方法的 Javadoc 注释

  • Javadoc comments on public interfaces, classes, and main methods

  • 文件夹组织以及模块和子模块的分解和命名

  • Folder organization and decomposition and naming of modules and submodules

相反,外部文档的示例包括以下内容:

In contrast, examples of external documentation include the following:

  • 自述文件和类似的文本文件

  • README and similar text files

  • 有关该项目的任何 HTML 或 Microsoft Office 文档

  • Any HTML or Microsoft Office document about the project

优先选择内部文档

Preferring Internal Documentation

请记住我之前所说的:放置有关事物的文档的最佳位置是事物本身。

Remember what I said earlier: The best place to put documentation about a thing is on the thing itself.

正如您将在本书中看到的那样,我绝对赞成内部文档,并在需要发布更传统文档的情况下加上足够的自动化。我建议默认选择内部文档,至少对于所有有定期更改风险的知识来说是这样。

As you’ll see throughout this book, I’m definitely in favor of internal documentation, coupled with just enough automation for cases where it’s necessary to publish more traditional documents. I suggest choosing internal documentation by default, at least for all knowledge that’s at risk of changing regularly.

即使对于稳定的知识,我也首先推荐内部文档,并且仅当有明显的附加值时我才会选择制作外部文档,例如必须具有最大吸引力的文档(可能出于营销原因)。在这种情况下,我建议手工制作的幻灯片、经过精心手动布局的图表以及吸引人的图片。使用外部文档的目的是能够为最终文档添加人性化的感觉,因此我会使用 Apple Keynote 或 Microsoft PowerPoint,选择或创建精美的高质量图片,并在面板上测试文档的有效性的同事,以确保它受到好评。

Even for stable knowledge, I recommend internal documentation first, and I would choose to do external documentation only when there’s clearly value added, such as with a documentation that must be maximally attractive (perhaps for marketing reasons). In that case, I suggest hand-crafted slides, diagrams with careful manual layout, and appealing pictures. The point of using external documentation would be to be able to add a human feel to the final document, so I’d use Apple Keynote or Microsoft PowerPoint, select or create beautiful quality pictures, and beta test the effectiveness of the documentation on a panel of colleagues to make sure it’s well received.

请注意,吸引力和幽默可能很难自动化或编码到正式文档中,但这也不是不可能的。

Note that appeal and humor can be hard to automate or to encode into formal documentation, but it’s not impossible either.

现场记录

In Situ Documentation

内部文档,也称为现场文档,是指“处于自然或原始位置或地点”的文档。10

Internal documentation, also an in-situ documentation, means documentation that is “in the natural or original position or place.”10

10.经许可。来自 Merriam-Webster.com © 2019 Merriam-Webster, Inc. https://www.merriam-webster.com/dictionary/insitu

10.By Permission. From Merriam-Webster.com © 2019 by Merriam-Webster, Inc. https://www.merriam-webster.com/dictionary/insitu.

这意味着文档不仅使用相同的实现技术,而且还直接混合到构建产品的工件内的源代码中。就地意味着将有关事物所在位置的附加知识带来,例如在源代码中而不是在远程位置。

This implies that the documentation is not only using the same implementation technology, but is also directly mixed into the source code, within the artifact that built the product. In situ means bringing the additional knowledge about a thing where the thing is located, for example within the source code rather than in a remote location.

这种类型的文档对于开发人员来说很方便。与设计用户界面一样,术语“原位”意味着可以在不进入另一个窗口的情况下执行特定的用户操作,而可以在不进入另一个文件或另一个工具的情况下执行文档的使用和编辑。

This type of documentation is convenient for developers. As in designing user interfaces, where the term in situ means that a particular user action can be performed without going to another window, consuming and editing the documentation can be performed without going to another file or to another tool.

机器可读文档

Machine-Readable Documentation

好的文档侧重于高级知识,例如代码之上的设计决策以及这些决策背后的基本原理。我们通常认为这种知识只有人们才感兴趣,但即使是工具也可以利用它。由于内部文档是使用实现技术来表达的,因此通常可以通过工具来解析。这为帮助开发人员完成日常任务的工具提供了新的机会。特别是,它可以实现知识的自动处理,以进行管理、整合、格式转换、自动发布或协调。

Good documentation focuses on high-level knowledge like the design decisions on top of the code and the rationale behind these decisions. We usually consider this kind of knowledge to be of interest only to people, but even tools can take advantage of it. Because internal documentation is expressed using implementation technologies, it can usually be parsed by tools. This opens new opportunities for tools to assist developers in their daily tasks. In particular, it enables automated processing of knowledge for curation, consolidation, format conversion, automated publishing, or reconciliation.

特定知识与通用知识

Specific Versus Generic Knowledge

有些知识是特定于您的公司、特定系统或业务领域的,有些知识是通用的并与业内许多其他公司的许多其他人共享。

There is knowledge that is specific to your company, your particular system, or your business domain, and there is knowledge that is generic and shared with many other people in many other companies in the industry.

有关编程语言、开发人员工具、软件模式和实践的知识属于通用知识类别。示例包括 DDD、模式、使用 Puppet 和 Git 教程进行持续集成。

Knowledge about programming languages, developers’ tools, software patterns, and practices belongs to the generic knowledge category. Examples include DDD, patterns, continuous integration using Puppet and Git tutorials.

关于成熟商业行业的知识也是通用知识。即使在竞争非常激烈的领域,例如金融定价或电子商务中的供应链优化,大多数知识都是公开的并且可在行业标准书籍中找到,只有一小部分业务知识是特定的和机密的,并且仅适用于特定的领域。就在那时。

Knowledge about mature business industry sectors is also generic knowledge. Even in very competitive areas like pricing in finance or supply chain optimization in e-commerce, most of the knowledge is public and available in industry-standard books, and only a small part of the business knowledge is specific and confidential— and only for a while at that.

例如,每个业务领域都有自己的基本阅读清单,并且可能有一本通常被称为该领域“圣经”的书(例如约翰·C·赫尔(John C Hull)的《期权、期货和其他衍生品》、《物流和金融)供应链管理,作者:马丁·克里斯托弗 (Martin Christopher)。

For example, each business domain has its own essential reading list, and it might have a book that is often referred to as “The Bible” of that field (for example, Options, Futures, and Other Derivatives by John C Hull, Logistics and Supply Chain Management by Martin Christopher).

好消息是通用知识已经记录在行业文献中。有书籍、博客文章和会议演讲对此进行了很好的描述。有标准词汇可以谈论它。有一些培训可以让知识渊博的人更快地学习它。

The good news is that generic knowledge is already documented in the industry literature. There are books, blog posts, and conference talks that describe it quite well. There are standard vocabularies to talk about it. There are trainings available to learn it faster from knowledgeable people.

学习通用知识

Learning Generic Knowledge

您还可以通过工作、阅读书籍以及参加培训和会议来学习通用知识。这只需要几个小时,而且你事先知道你要学什么、需要多长时间以及需要花多少钱。学习通用知识就像去商店买食物一样容易。

You also learn generic knowledge by doing your job, as well as by reading books and attending trainings and conferences. This only takes a few hours, and you know beforehand what you’re going to learn, how long it will take, and how much it will cost. It’s as easy to learn generic knowledge as it is to go to the store to buy food.

通用知识是一个已解决的问题。这些知识是现成的,可供每个人重复使用。当您使用它时,您只需链接到权威来源即可完成记录。这就像记下互联网链接或参考书目一样简单。

Generic knowledge is a solved problem. This knowledge is ready-made, ready to be reused by everyone. When you use it, you just have to link to an authoritative source, and you’re done documenting. This is as simple as noting an Internet link or a bibliographic reference.

专注于特定知识

Focusing on Specific Knowledge

使用特定知识的文档并从培训中学习通用知识。

Use documentation for specific knowledge and learn the generic knowledge from trainings.

特定知识是您的公司或团队拥有但尚未与同一行业的其他同行共享的知识。学习这些知识比学习通用知识更昂贵;这需要时间练习和犯错误。这是最值得关注的知识。

Specific knowledge is the knowledge your company or team has that is not (yet) shared with other peers in the same industry. This knowledge is more expensive to learn than generic knowledge; it takes time practicing and making mistakes. This is the kind of knowledge that deserves most attention.

具体的知识是有价值的,并且无法找到现成的,所以这是你必须照顾的知识。特定的知识值得您和您的同事付出最大的努力。作为一名专业人士,您应该了解足够的通用行业标准知识,以便能够专注于增长针对您特定目标的知识。

Specific knowledge is valuable and cannot be found ready-made, so it’s the kind of knowledge you have to take care of. Specific knowledge deserves the biggest efforts from you and your colleagues. As a professional, you should know enough of the generic, industry standard knowledge to be able to focus on growing the knowledge that’s specific to your particular ambitions.

因此:确保每个人都接受过行业通用知识的培训。然后将所有文档工作集中在特定知识上。

Therefore: Make sure everyone is trained on the generic knowledge in your industry. Then focus any documentation effort on specific knowledge.

确保文档准确性

Ensuring Documentation Accuracy

只有存在保证其准确性的机制,您才可以信任文档。

You can trust documentation only if there is a mechanism to guarantee its accuracy.

当谈到文档时,主要问题通常是它不准确,通常是因为过时。并非始终 100% 准确的文档是不可信任的。一旦您知道文档有时会产生误导,它就会失去可信度。它可能仍然有点用,但需要更多时间才能找出其中的正确和错误。当谈到创建文档时,当您知道它不会长期准确时,就很难投入时间。它的寿命是一个很大的动力杀手。

When it comes to documentation, the main problem is often that it’s not accurate, usually because of obsolescence. Documentation that is not 100% accurate all the time cannot be trusted. As soon as you know documentation can be misleading from time to time, it loses its credibility. It might still be a bit useful, but it will take more time to find out what’s right and what’s wrong in it. And when it comes to creating documentation, it’s hard to dedicate time to it when you know it won’t be accurate for long; its lifespan is a big motivation killer.

但更新文档是有史以来最不受重视的任务之一。这并不有趣,而且似乎没有回报。但是,如果您认真对待它并决定使用精心选择的机制来始终确保准确性来解决它,那么您可以拥有很好的文档。

But updating documentation is one of the most unappreciated tasks ever. It is not interesting and doesn’t seem rewarding. However, you can have nice documentation if you take it seriously and decide to tackle it with a well-chosen mechanism to enforce accuracy at all times.

因此:您需要考虑如何解决文档的准确性问题。

Therefore: You need to think about how you address the accuracy of your documentation.

可靠文档的准确性机制

Accuracy Mechanism for Reliable Documentation

正如前面提到的,可以信任的权威知识已经存在于某处,通常以源代码的形式存在。因此,重复的知识是有问题的,因为它会成倍增加更新知识以跟上变化的成本。当然,这适用于源代码,也适用于所有其他工件。我们通常将“设计”称为确保更改在任何时间点都保持低成本的学科。当然,我们需要代码设计,并且我们需要相同的设计技能来处理有关文档的所有内容。

As mentioned earlier, authoritative knowledge that can be trusted already exists somewhere, usually in the form of source code. Therefore, duplicated knowledge is problematic because it multiplies the cost of keeping it updated to keep pace with changes. This applies to source code, of course, and this applies to every other artifact, too. We usually call “design” the discipline of making sure that changes remain cheap at any point in time. We need design for the code, of course, and we need the same design skills for everything about documentation.

一个好的文档方法是设计问题。需要设计技能来设计始终准确的文档,同时又不会减慢软件开发工作的速度。

A good approach for documentation is a matter of design. It takes design skills to design documentation that is always accurate, without slowing down the software development work.

由于知识随时可能发生变化,因此有多种方法可以保持文档的准确性。以下各节对它们进行了描述,按照从最理想到最不理想的顺序排列,第 3 章知识利用”对它们进行了扩展。

With knowledge that can change at any time, there are a number of approaches to keeping documentation accurate. They are described in the following sections, ordered from the most desirable to the least desirable, and Chapter 3, “Knowledge Exploitation,” expands on them.

考虑保存在单一来源中的权威知识。这些知识只有能够阅读文件的人才能访问。例如,对于开发人员来说,源代码本身就是一个自然的文档,有了好的代码,就不需要其他任何东西。例如,用于配置 Maven 或 NuGet 等依赖项管理工具的所有依赖项列表的清单是依赖项列表的自然权威文档。只要这些知识只是开发人员感兴趣的,就可以了;不需要发布机制来使其他受众能够访问知识。

Consider a piece of knowledge kept in a single source that is authoritative. This knowledge is accessible only to the people who can read the files. For example, source code is a natural documentation of itself for developers, and with good code, there’s no need for anything else. For example, a manifest to configure the list of all dependencies for a dependency management tool like Maven or NuGet is natural authoritative documentation for the list of dependencies. As long as this knowledge is only of interest for developers, it’s just fine as it is; there’s no need for a publishing mechanism to make the knowledge accessible to other audiences.

具有发布机制的单一来源

单一采购是尽可能受到青睐的方法。通过单一来源,知识被保存在一个权威的单一来源中。由于采用了自动发布机制,它可以以各种形式作为已发布和版本化的文档提供。任何时候有变化,都会在那里更新,而且只在那里更新。

Single sourcing is the approach to favor whenever possible. With single sourcing, knowledge is kept in a single source that is authoritative. It’s made available in various forms as published and versioned documents, thanks to an automated publishing mechanism. Any time there’s a change, it’s updated there and only there.

例如,源代码和配置文件通常是许多知识的天然权威来源。必要时,来自单一真理来源的知识会被提取并以另一种形式发布,但很明显,只有一个地方是权威的。发布机制也应该自动化,以便频繁运行;自动化可以防止引入手动文档中常见的错误。

As an example, source code and configuration files are often the natural authoritative homes for a lot of knowledge. When necessary, the knowledge from such a single source of truth is extracted and published in another form, but it remains clear that there is only one place that is authoritative. The publishing mechanism should also be automated to be run frequently; automation prevents the introduction of errors that is common with manual documentation.

即使没有额外的注释,Javadoc 也是这种方法的一个很好的例子:参考文档就是源代码本身,由 Javadoc Doclet 解析,并且它会自动发布为网站,供每个人浏览接口、类和接口的结构。方法,包括类层次结构,以方便且始终准确的方式。

Even without the additional comments, Javadoc is a good example of this approach: The reference documentation is the source code itself, as parsed by the Javadoc Doclet, and it’s published automatically as a website for everyone to browse the structure of interfaces, classes, and methods, including the class hierarchies, in a convenient and always accurate manner.

具有传播机制的冗余源

知识可能会在不同的地方重复,但可靠的工具可以自动将一个地方的任何变化传播到其他地方。IDE 中的自动重构是这种方法的最佳示例。类名、接口名和方法名在代码中随处重复,但重命名它们很容易,因为 IDE 知道如何可靠地追踪每个引用并正确更新它。这比使用“查找替换”要优越得多,也更安全,在“查找和替换”中,您可能会面临错误替换随机字符串的风险。

Knowledge may be duplicated in various places, but reliable tools can automatically propagate any change in one place to every other place. Automated refactorings in your IDE are the best examples of this approach. The class names, interface names, and method names are repeated everywhere in the code, but it’s easy to rename them because the IDE knows how to reliably chase every reference and update it correctly. This is far superior to and safer than using Find and Replace, where you run the risk of replacing random strings by mistake.

同样,AsciiDoc 等文档工具提供内置机制来声明属性,然后您可以将这些属性嵌入到文本中的任何位置。借助内置的包含和替换功能,您可以在一处重命名和进行更改,并将更改传播到许多地方,无需任何成本。

Similarly, documentation tools such as AsciiDoc offer built-in mechanisms to declare attributes that you can then embed everywhere in the text. Thanks to the built-in include and substitution features, you can rename and make changes in one place and propagate the change to many places at no cost.

具有协调机制的冗余源

如果知识是在两个来源中声明的,那么一个来源可能会发生变化,而另一个来源不会发生变化——这是一个问题。需要一种机制来检测两个源不匹配的情况。这种协调机制应该自动化并经常运行,以确保永久一致性。

If knowledge is declared in two sources, one source may change without the other changing—and that’s a problem. There’s a need for a mechanism to detect whenever the two sources don’t match. Such a reconciliation mechanism should be automated and run frequently to ensure permanent consistency.

使用 Cucumber 等自动化工具的 BDD 就是这种方法的一个示例。在这种情况下,代码和场景是知识的两个来源,它们都描述了相同的业务行为。每当运行场景的测试失败时,就表明场景和代码不再同步。

BDD with automation tools such as Cucumber is an example of this approach. In this case, the code and the scenarios are the two sources of knowledge, and they both describe the same business behavior. Whenever a test running the scenarios fails, it’s a signal that the scenarios and the code are no longer in sync.

反模式:人类奉献

人类奉献是一种反模式。如果知识在不同的地方重复,有时会留给团队中的人员通过大量的奉献和艰苦的工作来确保一切始终保持一致。实际上,这是行不通的,也不是推荐的方法。

Human dedication is an anti-pattern. If knowledge is duplicated in various places, sometimes it’s left to people on the team to make sure everything remains consistent at all times through a lot of dedication and hard grunt work. In practice, this does not work, and it is not a recommended approach.

当文档不需要准确性机制时

When Documentation Does Not Need an Accuracy Mechanism

在某些情况下,例如在以下部分中,您的文档不需要准确性机制。

In some cases, such as in the following sections, you don’t need an accuracy mechanism for your documentation.

一次性知识

有时准确性并不是问题,因为记录的知识将在使用后几小时或几天内被处理掉。这种短暂的知识不会老化,也不会进化,因此不用担心它的时效性。一致性——只要它只使用很短的一段时间,并且在使用后立即处理掉。例如,一旦手头的任务完成,结对编程中两人之间的对话以及 TDD 中婴儿学步期间编写的代码就不再重要。

Sometimes accuracy is just not a concern because the knowledge recorded will be disposed of within hours or a few days after use. This kind of transient knowledge does not age and does not evolve, and hence there’s no concern about its consistency—as long as it’s used for only a short period of time and actually disposed of immediately after use. For example, conversations between the pair in pair programming and the code written during baby steps in TDD don’t matter once the task at hand is done.

过去的账目

对过去事件的描述(例如博客文章)不受准确性问题的影响,因为读者很清楚,不能保证文本永远准确。例如,帖子的要点可能是描述发生的情况,包括当时的想法和相关的情绪。

An account of past events, such as a blog post, is not subject to issues of accuracy because it is clear for the reader that there is no promise of the text being accurate forever. The point of the post may be, for example, to describe a situation as it happened, including the thinking at the time and the related emotions.

在某个时间点准确且在该时间点的上下文中记录的此类知识不被视为过时的文档。随着时间的推移,博客文章中的知识确实会过时,但这不是问题,因为它显然是在博客文章的上下文中,日期和故事显然是过去的。这是一种以持久的方式归档工作片段和故事背后的大创意的聪明方法,而不是假装它是常青的。博客文章不会误导任何人认为这是新信息,因为很明显,它是对过去反思的描述。作为一个植根于过去的故事,它始终是一个准确的故事,即使您不能信任可能引用的特定代码或示例。就像读一本历史书,有很多宝贵的教训值得学习,

Such knowledge that is accurate at a point in time and that’s recorded in the context of that point in time is not considered obsolete documentation. The knowledge in the blog post does get outdated over time, but this is not a problem as it’s clearly in the context of a blog post with a date and a story that’s clearly in the past. This is a smart way to archive episodes of work and the big idea behind a story in a persistent fashion, without pretending it is evergreen. A blog post won’t mislead anyone to think that it’s new information as it’s clear that it’s an account of a past reflection. As a story anchored in the past, it’s always an accurate story, even if you can’t trust the particular code or examples that may be quoted. It’s like reading a book on history, and there are a lot of precious lessons to be learned, regardless of the context in which they happened.

对于过去的帐户来说,最糟糕的情况是,当当时的担忧不再是问题时,它可能会变得无关紧要。

The worst that can happen to an account from the past is that it might become irrelevant, when the concerns of that time are no longer concerns.

挑战你的文档的大问题

Big Questions to Challenge Your Documentation

制作文档的每一分钟都浪费在其他事情上。这是增值吗?这是最重要的吗?

Every minute crafting documents is a minute lost to other things. Is this adding value? Is this most important?

—推特上的@dynamoben

—@dynamoben on Twitter

想象一下,您的老板或客户要求提供“更多文档”。需要提出并回答许多重要问题才能决定下一步做什么。这些问题背后的目标是确保从长远来看您将尽可能高效地利用时间。

Imagine that your boss or a customer asks for “more documentation.” A number of important questions need to be asked and answered to decide what to do next. The goal behind these questions is to make sure you’re going to use your time as efficiently as possible in the long run.

以下各节列出的重要问题的提出顺序取决于具体情况,您可以随意跳过或重新排列问题。以下各节解释了确定如何编写文档所涉及的思维过程,一旦您理解了它,您就可以使该过程成为您自己的过程。

The order in which you ask the important questions listed in the following sections depends on the situation, and you may skip or rearrange the questions at will. The following sections explain the thought process involved in determining how to do documentation, and once you understand it, you can make the process your own.

质疑是否需要文档

Questioning the Need for Documentation at All

文档本身并不是目的;这是达到必须确定的目的的手段。除非你理解目标,否则你将无法做出有用的东西。所以第一个问题是:

Documentation is not an end in itself; it’s a mean for a purpose that must be identified. You won’t be able to make something useful unless you understand the goal. So the first question is:

为什么我们需要这个文档?

Why do we need this documentation?

如果没有轻易得到答案,那么您肯定还没有准备好开始在额外的文档上投入精力。你应该暂时搁置这个话题,直到你更加了解为止。您不想将时间浪费在不明确的目标上。

If no answer comes easily, then you’re definitely not ready to start investing effort in additional documentation. You should put the topic on hold until you know better. You don’t want to waste time on ill-defined objectives.

那么下一个问题就随之而来:

Then the next question immediately follows:

目标受众是谁?

Who’s the intended audience?

如果答案不清楚或听起来像“每个人”,那么你还没有准备好开始做任何事情。有效的文档必须针对确定的受众。即使是关于“每个人都应该知道”的事情的文档也必须针对受众,例如“对业务领域只有肤浅了解的非技术人员”。

If the answer is unclear or sounds like “everyone,” then you’re not ready to start doing anything. Efficient documentation must target an identified audience. Even documentation about things “that everyone should know” has to target an audience, such as “nontechnical people with only a superficial knowledge of the business domain.”

现在,您仍决心避免浪费时间,准备好回答第一个问题文档。

Now, still determined to avoid wasting time, you’re ready for the first question of documentation.

文档的第一个问题

The First Question of Documentation

我们真的需要这个文档吗?

Do we really need this documentation?

有人可能会想要创建一个仅与他或她自己感兴趣的主题相关的文档,或者仅与他或她正在研究该主题的时间相关的文档。也许向 wiki 添加一段文字也没有多大意义。但要求提供文件还有另一个更糟糕的原因。

Someone might be tempted to create documentation on a topic that is only of interest for himself or herself or that is relevant only for the time he or she is working on it. Perhaps it does not make much sense to even add a paragraph to the wiki. But there’s another, and worse, reason for asking for documentation.

由于缺乏信任而需要文档

Need for Documentation Because of Lack of Trust

关于文档的第一个问题的答案可能听起来像“我需要文档,因为我担心你的工作没有我想要的那么多,所以我需要查看可交付成果以确保你工作足够努力。” 在这种情况下,主要问题不是文档问题。

The answer to the first question of documentation may sound something like “I need documentation because I’m afraid you don’t work as much as I’d like, so I need to see deliverables to make sure you work hard enough.” In this case, the main issue is not a matter of documentation.

正如 Matt Wynne (@mattwynne) 和 Seb Rose (@sebrose) 在 2013 年 BDD eXchange 会议上所说:“需要细节可能表明缺乏信任。” 在这样的在一个案例中,缺乏文件只是表象,根本问题是缺乏信任。这是一个足够严重的问题,你应该停止阅读这本书并尝试找到改善这种情况的方法。仅靠任何文档都无法从一开始就解决缺乏信任的问题。然而,由于交付价值通常是建立信任的好方法,因此合理的文档在补救措施中发挥着辅助作用。例如,使工作更加可见可能有助于建立信任,并且是一种文档形式。

As the duo Matt Wynne (@mattwynne) and Seb Rose (@sebrose) said at the BDD eXchange conference in 2013: “Need for detail might indicate lack of trust.” In such a case, lack of documentation is just a symptom, and the root issue is lack of trust. This is a serious enough issue that you should stop reading this book and try to find ways to improve the situation. No amount of documentation alone can fix lack of trust in the first place. However, since delivering value often is a good way to build trust, sensible documentation has a side role in a remediation. For example, making the work more visible may help build trust and is a form of documentation.

即时文档,或未来知识的廉价选择

Just-in-Time Documentation, or a Cheap Option on Future Knowledge

如果您需要文档,您可能实际上并不立即需要它。因此,还有一个文档的第一个问题。

If you need documentation, you might not actually need it right away. Hence, there is another first question of documentation.

文档的其他第一个问题

The Other First Question of Documentation

我们现在真的需要这个文档吗?

Do we really need this documentation now?

创建文档是有成本的,而未来的收益是不确定的。当您无法确定某人将来是否需要该信息时,好处是不确定的。

Creating documentation has a cost, and the benefit in the future is an uncertain benefit. The benefit is uncertain when you cannot be sure someone will have the need for the information in the future.

多年来我在软件开发中学到的一件事是,人们在预测未来方面是出了名的糟糕。通常人们只是打赌,但他们的赌注往往是错误的。因此,使用多种策略来确定何时需要进行文档记录非常重要:

One thing I’ve learned over the years in software development is that people are notoriously bad at anticipating the future. Usually people can just bet, and their bets are often wrong. As a consequence, it is important to use a number of strategies to determine when it’s important to do documentation:

  • 及时:仅在真正需要时添加文档。

  • Just-in-time: Add documentation only when really needed.

  • 前期成本低廉:立即添加一些文档,成本非常低。

  • Cheap upfront: Add a little documentation now, at a very low cost.

  • 前期费用昂贵:立即添加文档,即使创建它需要时间。

  • Expensive upfront: Add documentation now, even if it takes time to create it.

准时制

考虑到它在未来是否有用的不确定性,您可能会认为现在记录的成本不值得。在这种情况下,您可能会推迟编写文档,直到确实有必要为止。通常,等待某人启动文档工作是一个好主意。在拥有许多利益相关者的大型项目中,您甚至可能决定等待第二个或第三个请求,然后再决定是否值得投入时间和精力来创建文档。

You may decide that the cost of documenting now is not worth it, given the uncertainty that it will be useful in the future. In such a case, you might put off doing documentation until it becomes really necessary. Typically, it is a good idea to wait for someone to initiate a documentation effort. On a big project with lots of stakeholders, you might even decide to wait for a second or third request before deciding it’s worth investing time and effort in creating documentation.

请注意,这假设当需要分享知识时,您在团队中的某个地方仍然拥有可用的知识。它还假设未来记录的工作量与现在相比不会太大。

Note that this assumes that you will still have the knowledge available somewhere in the team when the time comes to share it. It also assumes that the effort of documenting in the future will not be too great compared to what it would be right now.

便宜的预付款

您可能会认为现在记录的成本是如此之低,以至于不值得推迟到以后,即使它从未真正使用过。当你对知识记忆犹新,并且你冒着以后会更难记住所有利害关系和重要细节的风险时,这一点尤其重要。当然,如果您有廉价的方法来预先创建文档,那么提前创建文档是有意义的,正如您稍后将看到的。

You might decide that the cost of documenting right now is so low that it’s not worth deferring it for later, even if it’s never actually used. This is especially relevant when the knowledge is fresh in mind and you run the risk that it will be much harder later to remember all the stakes and important details. And, of course, creating documentation upfront makes sense if you have cheap ways to do it, as you’ll see later.

前期费用昂贵

您可能会认为押注于未来对这些知识的需求是值得的,并选择立即创建文档,即使这样做并不便宜。存在可能会造成浪费的风险,但您可能会乐意承担这一风险 - 希望有某种经过证实的原因(例如,准则或合规性要求、多个人高度确信这是必要的)。

You might decide that it’s worth it to bet on the future need for this knowledge and choose to create documentation right now, even if doing so is not cheap. There’s the risk it might be a waste, but you might be happy to take this risk—hopefully for some substantiated reason (for example, guidelines or compliance requirement, high confidence from more than one person that it’s necessary).

重要的是要记住,现在围绕文档所做的任何努力也会对工作质量产生影响,因为它把重点放在如何完成和为什么完成上,就像审查一样。这意味着,即使它将来永远不会使用,但现在至少可以有用一次,以便清楚地思考决策及其背后的基本原理。

It’s important to keep in mind that any effort around documentation right now also has an impact on the quality of the work because it put the focus on how it’s done and why and acts like a review. This means that even if it’s never used in the future, it can be useful at least once, right now, for the sake of thinking clearly about the decisions and the rationale behind them.

质疑传统文档的必要性

Questioning the Need for Traditional Documentation

假设出于确定的目的和确定的受众确实需要额外的文档,那么您现在已经准备好处理第二个问题的文档。

Assuming that there’s a genuine need for additional documentation, for an identified purpose, and for an identified audience, you’re now ready for the second question of documentation.

文件的第二个问题

The Second Question of Documentation

我们可以通过对话或合作来分享知识吗?

Could we just share knowledge through conversations or working together?

传统文档永远不应该成为默认选择,因为除非绝对必要,否则太浪费了。当需要将知识从某些人转移给其他人时,最好通过简单的交谈来完成——通过提出和回答问题而不是交换书面文件。

Traditional documentation should never be the default choice, as it’s too wasteful unless absolutely necessary. When there is a need for knowledge transfer from some people to other people, this is best done by simply talking—by asking and answering questions instead of exchanging written documents.

集体工作和频繁的对话是一种特别有效的记录形式。结对编程、交叉编程、敏捷中的三个朋友和群体编程等技术完全改变了文档方面的游戏规则,因为人与人之间的知识转移是连续进行的,同时知识是在一个平台上创建或应用的。任务。

Working collectively, with frequent conversations, is a particularly effective form of documentation. Techniques such as pair programming, cross-programming, the three amigos in agile, and mob-programming totally change the game with respect to documentation, as knowledge transfer between people is done continuously and at the same time the knowledge is created or applied on a task.

对话和集体工作是首选的文档形式,尽管有时它们还不够。有时确实需要将知识形式化。

Conversations and working collectively are the preferred forms of documentation, although sometimes they are not enough. Sometime there’s a genuine need to have the knowledge formalized.

挑战正式化文档的需求

Challenging the Need for Formalized Documentation

一定要坚持吗?是否必须与大量受众分享?这是批判性知识吗?

Does it have to be persistent? Does it have to be shared to a large audience? Is it critical knowledge?

如果这些问题的答案都是“否”,那么对话和集体工作就足够了,不需要更正式的文档。

If the answer to each of these questions is “no,” conversations and working collectively should be enough, and there is no need for more formal documentation.

当然,如果你问经理这些问题,你很可能会得到“是”的回答,因为这是一个安全的选择。你做更多的事就不会错,对吧?这有点像设置任务的优先级;许多人对所有事情都设置了高优先级标志,这使得高优先级变得毫无意义。对于文档来说,看似安全的选择会带来更高的成本,进而危及项目。安全的选择实际上是以平衡的方式考虑这三个问题,而不是自动回答“是”或“否”。

Of course, if you ask a manager these questions, you’re likely to be answered “yes” just because it’s a safe choice. You can’t be wrong by doing more, right? It’s a bit like setting the priority on tasks; many people put a high priority flag on everything, which then makes high priority meaningless. With documentation, what seems to be the safe choice carries a higher cost, which can in turn endanger the project. The safe choice is really to consider these three questions in a balanced way rather than automatically answering “yes” or “no.”

即使对于必须与大量受众共享、必须长期保留或至关重要的知识,也有多种文档选项:

Even for the knowledge that must be shared to a large audience, that must be kept persistent for the long term or that is critical, there are several documentation options:

  • 全体观众出席的全体会议,或希望观众做笔记的讲座式会议演讲

  • Plenary meeting with the full audience attending, or a lecture-style conference talk with the audience hopefully taking notes

  • 播客或视频,例如录制的会议演讲或录制的采访

  • Podcast or video, like a recorded conference talk or a recorded interview

  • 自记录或通过内部文档方法增强的工件

  • Artifacts that are self-documented or that are augmented in an internal documentation approach

  • 手工编写的文档

  • Manually written document

关键是,即使拥有特别重要的知识,手动编写文档也不一定是默认选择。

The point is that, even with particularly important knowledge, manually written documentation does not have to be the default choice.

立即减少额外工作

Minimizing Extra Work Now

假设您有合理的需要以正式的形式保留一些知识。因为,正如您所知,大多数知识已经以某种形式存在于某个地方,您需要回答另一个问题。

Say that you have a legitimate need to keep some knowledge in a formal form. Because, as you’ve learned, most knowledge already exists somewhere, in some form, you need to answer another question.

知识定位问题

The Knowledge Location Question

现在知识在哪里?

Where’s the knowledge right now?

如果知识只存在于人们的头脑中,那么它需要在某个地方进行编码——作为文本、代码、元数据或其他东西。如果知识已经在某处表示,那么我们的想法是尽可能地使用它(知识利用)或重用它(知识增强)。

If knowledge is only in the heads of the people, then it needs to be encoded somewhere—as text, code, metadata, or something else. If the knowledge is already represented somewhere, the idea is to use it (knowledge exploitation) or reuse it (knowledge augmentation) as much as possible.

您也许能够使用源代码、配置文件、测试、应用程序在运行时的行为以及可能涉及的各种工具的内存中的知识。此过程将在以下章节中详细描述,涉及提出以下问题:

You might be able to use the knowledge that’s in the source code, in the configuration files, in the tests, in the behavior of the application at runtime, and perhaps in the memory of the various tools involved. This process, which is described in detail in the following chapters, involves asking the following questions:

  • 这些知识是可利用的,还是被混淆的,或者是不可恢复的?

  • Is the knowledge exploitable, or obfuscated, or unrecoverable?

  • 知识太丰富了吗?

  • Is the knowledge too abundant?

  • 目标受众是否可以获取这些知识?

  • Is the knowledge accessible for the intended audience?

  • 知识是集中在一个地方还是分散的?

  • Is the knowledge in one single place or fragmented?

  • 缺少什么才能使知识 100% 明确?

  • What is missing that would make the knowledge 100% explicit?

当知识不完全存在或太隐晦而无法使用时,游戏就变成寻找一种将知识直接添加到产品源中的方法。这是第 4 章“知识增强”的重点

When the knowledge is not fully there or is too implicit to be used, then the game becomes finding a way to add the knowledge directly into the source of the product. That is the focus of Chapter 4, “Knowledge Augmentation.”

最大限度地减少以后的额外工作

Minimizing Extra Work Later

仅创建一次文档是不够的;还要创建文档。您必须考虑如何随着时间的推移保持其准确性。因此,仍然存在一个重要的问题。

It’s not enough to create documentation once; you must consider how to keep it accurate over time. Therefore, an important question remains.

知识稳定性问题

The Knowledge Stability Question

这些知识的稳定性如何?

How stable is this knowledge?

稳定的知识很容易,因为你可以忽略它的维护问题。另一方面,生活知识具有挑战性。它可以经常或随时更改,并且您不希望一遍又一遍地更新多个工件和文档。

Stable knowledge is easy because you can ignore the question of its maintenance. On the other end of the spectrum, living knowledge is challenging. It can change often or at any time, and you don’t want to update multiple artifacts and documents over and over.

变化率是关键标准(见图1.10)。多年来稳定的知识可以用任何传统形式来处理,例如手动书写文本并将其打印在纸上。多年稳定的知识甚至可以在一定程度的重复中幸存下来,因为它不需要更新。

The rate of change is the crucial criterion (see Figure 1.10). Knowledge that is stable over years can be taken care of with any traditional form, such as writing text manually and printing it on paper. Knowledge that is stable over years can even survive some amount of duplication because it will not need to be updated.

说明知识变化率的图形是关键标准,按顺序显示一张纸、乌龟、兔子和一些叶子。

图1.10 知识的变化率是关键标准

Figure 1.10 The rate of change of the knowledge is the key criterion

相比之下,每小时或更频繁地变化的知识无法承受传统形式的文档。要记住的关键问题是演化成本和维护文档的成本。更改源代码然后必须手动更新其他文档不是一种选择。

In contrast, knowledge that changes every hour or more often just cannot afford traditional forms of documentation. The key concerns to keep in mind are the cost of evolution and the cost of maintaining the documentation. Changing the source code and then having to update other documents manually is not an option.

这个过程将在接下来的章节中描述,涉及以下问题:

This process, which is described in the next chapters, involves the following questions:

  • 如果改变,同时改变什么?

  • If it changes, what changes at the same time?

  • 如果存在冗余知识,我们如何保持冗余源同步?

  • If there’s redundant knowledge, how do we keep the redundant sources in sync?

让活动变得有趣

Making an Activity Fun

要使一项活动可持续,就要让它变得有趣。

To make an activity sustainable, make it fun.

乐趣对于可持续实践很重要。如果某件事不有趣,你就不会想经常做它,这种做法也会逐渐消失。为了让练习持续下去,它们必须很有趣。这对于诸如文档之类的无聊主题尤其重要。

Fun is important for sustainable practices. If something is not fun, you’ll not want to do it very often, and the practice will progressively disappear. For practices to last, they need to be fun. This is particularly important with a boring topic such as documentation.

因此:选择尽可能有趣的活文档实践。如果某件事很有趣,那就多做一些,如果完全不有趣,就寻找替代方案,例如以其他方式或通过自动化解决问题。

Therefore: Choose living documentation practices that are as fun as possible. If something is fun, do more of it, and if it’s totally not fun, look for alternatives, such as solving the problem in another way or through automation.

这种对有趣活动的偏好显然是假设与人合作是有趣的,因为没有什么好的办法可以解决这个问题。例如,如果编码对您来说很有趣,您将尝试在代码中尽可能多地记录。这就是本书中许多建议背后的想法。如果将信息从一处复制到另一个是苦差事,那么它就适合自动化或寻找一种方法来避免移动数据。修复流程和自动化流程的一部分往往很有趣,因此这些也是您可能想做的事情(参见图 1.11)——这很幸运。

This preference for fun activities obviously assumes that working with people is on the fun side, because there’s no good way around that. For example, if coding is fun for you, you’ll try to document as much as possible in code. That’s the idea behind many suggestions in this book. If copying information from one place to another is a chore, then it’s a candidate for automation or for finding a way to avoid having to move data at all. Fixing a process and automating a part of a process tend to be fun, so these are also things you might feel like doing (see Figure 1.11)—and that’s lucky.

一个说明乐趣从自动化家务开始的人物展示了一个类似机器人的生物(拿着清洁拖把、装有一块布的水桶)在一个卡通人物旁边。

图 1.11 乐趣从自动化家务开始

Figure 1.11 Fun starts with automating the chores

乐趣与专业的结合

Mixing Fun and Professionalism

只要你专业,在工作中享受乐趣并没有什么错。这意味着尽最大努力解决重要问题、创造价值并降低风险。考虑到这一点,您就可以自由选择让您的生活更有趣的实践和工具。经过 18 年的编程生涯,我现在相信,在享受乐趣的同时,始终可以完成专业的工作。认为工作应该是无聊和不愉快的,因为它是工作,或者因为你得到报酬是为了补偿这种非常不愉快的事情,这种想法是愚蠢的。你付了一些钱来提供价值更多的价值。提供价值很有趣,表现得专业也很令人愉快。乐趣对于团队在愉快的氛围中高效工作至关重要。

There’s nothing wrong with having fun at work, as long as you’re professional in your work. This means doing your best to solve the problems that matter, delivering value, and reducing risk. With that in mind, you’re free to choose the practices and tools that make your life more fun. After 18 years in programming, I’m now confident it’s always possible to do professional work while having fun. The idea that work should be boring and unpleasant because it’s work or because you’re paid for it to compensate for this very unpleasantness is just stupid. You’re paid some money to deliver value that is worth even more money. Delivering value is fun, and behaving professionally is pleasant, too. And fun is essential for working efficiently as a team in a pleasant atmosphere.

文档重启

Documentation Reboot

这本书的标题可以是“文档 2.0:实时文档”“连续文档”或“无文档”。即使标题较短,《活文档》,本书的关键驱动力是从目的出发重新考虑我们编写文档的方式。从这里开始,适用解决方案的范围几乎是无限的。本书探讨了各种类别的实践和技术,并组织成近 100 种模式。表 1.1提供了这种模式语言的摘要。

This book could be titled Documentation 2.0: Living Documentation, Continuous Documentation, or No Documentation. Even with the shorter title Living Documentation, the key driver of this book is to reconsider the way we do documentation, starting from the purpose. From there, the universe of applicable solutions is nearly infinite. This book explores the universe of practices and techniques in various categories, organized into almost 100 patterns. Table 1.1 provides a summary of this language of patterns.

表 1-1 模式摘要

Table 1-1 Patterns Summary

图案

Pattern

简要描述;简介

Brief Description

重新思考文档

Rethinking Documentation

大多数知识已经存在

Most knowledge is already there

不需要记录系统本身已经记录的知识。

There is no need to record a piece of knowledge that is already recorded in the system itself.

更喜欢内部文档

Prefer internal documentation

存储文档的最佳位置是记录在案的事物本身。

The best place to store documentation is on the documented thing itself.

专注于特定知识

Focus on specific knowledge

使用特定知识的文档并从培训中学习通用知识。

Use documentation for the specific knowledge and learn the generic knowledge from trainings.

精准机制

Accuracy mechanism

只有存在保证其准确性的机制,您才可以信任文档。

You can trust documentation only if there is a mechanism to guarantee its accuracy.

有趣的活动

Fun activity

要使一项活动可持续,就要让它变得有趣。

To make an activity sustainable, make it fun.

知识利用

Knowledge Exploitation

单一来源出版

Single-source publishing

将知识保存在单一事实来源中,并在需要时从那里发布。

Keep the knowledge in one single source of truth and publish from there when needed.

对账机制

Reconciliation mechanism

如果知识在多个地方重复,则建立协调机制以立即发现不一致之处。

If knowledge is repeated in more than one place, set up a reconciliation mechanism to detect inconsistencies immediately.

整合分散的事实

Consolidation of dispersed facts

不同的事实放在一起就成为有用的知识。

Diverse facts put together become useful knowledge.

工具历史

Tools history

您的工具记录有关您的系统的知识。

Your tools record knowledge about your system.

现成的文档

Ready-made documentation

您所做的大部分工作已经记录在文献中。

Most of what you do is already documented in the literature.

知识扩充

Knowledge Augmentation

增强代码

Augmented code

当代码不能讲述完整的故事时,添加缺失的知识以使其完整。

When the code doesn’t tell the full story, add the missing knowledge to make it complete.

通过注释记录文档

Documentation by annotation

通过使用注释来扩展您的编程语言以用于文档目的。

Extend your programming language by using annotations for documentation purposes.

按惯例记录文件

Documentation by convention

依靠代码约定来记录知识。

Rely on code conventions to document knowledge.

全模块知识增强

Module-wide knowledge augmentation

跨越许多具有共同点的工件的知识最好集中在一个地方。

Knowledge that spans a number of artifacts that have something in common is best factored out in one place.

内在知识增强

Intrinsic knowledge augmentation

仅使用元素固有的知识来注释元素。

Only annotate elements with knowledge that is intrinsic to them.

嵌入式学习

Embedded learning

将更多知识放入代码中可以帮助维护人员在处理代码的同时学习。

Putting more knowledge into the code helps its maintainers learn while working on it.

边车文件

Sidecar files

当无法将注释放入代码中时,请将它们放入代码旁边的文件中。

When putting annotations within the code is not possible, put them into a file next to the code.

元数据数据库

Metadata database

当不可能在代码中添加注释时,请将它们保存在外部数据库中。

When putting annotations within the code is not possible, keep them in an outside database.

机器可访问的文档

Machine-accessible documentation

机器可访问的文档为工具在设计层面提供帮助提供了新的机会。

Documentation that is machine accessible opens new opportunities for tools to help at the design level.

记录你的理由

Record your rationale

决策背后的基本原理是增强代码的最重要的内容之一。

The rationale behind a decision is one of the most important things to augment the code with.

承认你的影响力

Acknowledge your influences

团队的主要影响力是理解他们所构建的系统的关键。

The major influences of a team are the keys for understanding the system they’ve built.

将消息作为综合文档提交

Commit messages as comprehensive documentation

精心编写的提交消息使每一行代码都有良好的记录。

Carefully written commit messages make each line of code well documented.

知识管理

Knowledge Curation

动态策展

Dynamic curation

即使所有的艺术品都已经在收藏中,但要利用它们举办展览仍然还有很多工作要做。

Even if all the works of art are already there in the collection, there is still work to be done to make an exhibition out of it.

突出显示核心11

Highlighted core11

该领域的某些元素比其他元素更重要。

Some elements of the domain are more important than others.

鼓舞人心的榜样

Inspiring exemplars

关于如何编写代码的最佳文档通常就是现有的最佳代码。

The best documentation on how to write code is often just the best code that is already there.

导游、观光地图12

Guided tour, sightseeing map12

通过导游或观光地图可以更轻松地快速发现新地方的最佳景点。

It is easier to quickly discover the best of a new place with a guided tour or a sightseeing map.

自动化文档编制

Automating Documentation

活文件

Living document

与其描述的系统同步发展的文档。

A document that evolves at the same pace as the system it describes.

生活词汇

Living glossary

与它所描述的系统同步发展的术语表,反映了代码中使用的领域语言。

A glossary that evolves at the same pace as the system it describes, reflecting the domain language used in the code.

生活图

Living diagram

可以在任何更改时再次生成图表,以便始终保持最新状态。

A diagram that can be generated again on any change so that it’s always up-to-date.

一张图/一个故事

One diagram/one story

一张图表应该只讲述一个特定的消息。

One diagram should tell only one specific message.

运行时文档

Runtime Documentation

可见测试

Visible test

测试可以以特定领域的符号产生视觉输出供人工审查。

Tests can produce visual output for human review in domain-specific notation.

可见的工作原理13

Visible workings13

工作软件可以有自己的文档

Working software can be its own documentation

 

 

在运行时。

at runtime.

反省的运作方式

Introspectable workings

你内存中的代码可以成为知识的源泉。

Your code in memory can be a source of knowledge.

可重构文档

Refactorable Documentation

代码作为文档

Code as documentation

大多数时候,代码就是它自己的文档。

Most of the time, the code is its own documentation.

集成文档

Integrated documentation

您的 IDE 已经满足了许多文档需求。

Your IDE already fulfills many documentation needs.

纯文本图表

Plain-text diagrams

不能是真正的活图的图表应该从纯文本文档创建,以使其维护更容易。

Diagrams that cannot be genuine living diagrams should be created from plain-text documents to make their maintenance easier.

稳定的文档

Stable Documentation

常青内容

Evergreen content

常青内容是指长时间保持有用而不改变的内容。

Evergreen content is content that remains useful without change for a long time.

常年命名

Perennial naming

喜欢比其他方案持续时间更长的命名方案。

Favor naming schemes that last longer than others.

关联知识

Linked knowledge

知识在连接时更有价值,只要连接稳定。

Knowledge is more valuable when it is connected, provided that the connections are stable.

链接注册表

Link registry

可以更改间接以修复单个位置中损坏的链接。

An indirection can be changed to fix broken links in a single place.

添加书签的搜索

Bookmarked search

链接搜索比直接链接更稳定。

A search made into a link is more stable than a direct link.

损坏的链接检查器

Broken link checker

尽快检测损坏的链接有助于保持文档的可信度。

Detecting broken links as soon as possible helps keep the documentation trusted.

投资于稳定的知识

Invest in stable knowledge

稳定的知识是一项可以在较长时期内获得回报的投资。

Stable knowledge is an investment that pays back over a longer period of time.

如何避免传统文档

How to Avoid Traditional Documentation

集体工作,持续分享知识

Working collectively as continuous knowledge sharing

集体工作是持续分享知识的机会。

Working collectively is an opportunity for continuous knowledge sharing.

咖啡机

Coffee machine

并非所有的知识交流都必须有计划

Not all exchange of knowledge has to be planned

沟通

communication

并管理。在轻松的环境中进行自发讨论通常效果更好,必须予以鼓励。

and managed. Spontaneous discussions in a relaxed environment often work better and must be encouraged.

思想沉淀

Ideas sedimentation

找出某条知识是否重要需要花费一些时间。

It takes some time to find out whether a piece of knowledge was important or not.

一次性文档

Throwaway documentation

有些文档仅在有限的时间内有用,然后才能删除。

Some documentation is useful for only a limited period of time before it can be deleted.

按需文档

On-demand documentation

记录您所看到的内容是必要的。

Document what you’ve seen is necessary to be documented.

惊讶报告

Astonishment report

新人的超能力正在带来新的视角。

Newcomers’ superpower is bringing a fresh perspective.

交互式文档

Interactive documentation

文档可以尝试模拟对话的交互性。

Documentation can try to emulate the interactivity of a conversation.

声明式自动化

Declarative automation

每次自动化软件任务时,您都应该抓住机会将其变成文档形式。

Every time you automate a software task, you should take the opportunity to make it a form of documentation as well.

强制指导方针

Enforced guidelines

如果最好的文档能够在正确的时间用正确的知识提醒您,那么甚至不需要阅读它。

The best documentation does not even have to be read if it can alert you at the right time with the right piece of knowledge.

行为受限

Constrained behavior

影响或约束行为而不是记录。

Influence or constrain the behavior instead of documenting.

可替换性第一

Replaceability first

可替换性设计减少了了解事物如何工作的需要。

Designing for replaceability reduces the need to know how things work.

一致性第一

Consistency first

保持一致可以减少对文档的需求。

Being consistent reduces the need for documentation.

超越文档:生活设计

Beyond Documentation: Living Design

听文档

Listen to the documentation

文档可以作为发现改进机会的信号。

Documentation can be a signal to spot opportunities for improvements.

可耻的文档

Shameful documentation

自由注释的存在通常是代码中可耻行为的信号。

The presence of a free comment is often a signal of a shameful behavior in the code.

深思熟虑的决策

Deliberate decision making

通往更好的设计和更好的文档的道路始于有意识地做出更多决策。

The path to better design and better documentation starts by making more decisions deliberately.

卫生透明度

Hygienic transparency

透明度可以提高卫生水平,因为污垢无法隐藏。

Transparency leads to greater hygiene because the dirt cannot hide.

词云

Word cloud

代码中标识符的词云应该揭示代码的含义。

A word cloud of the identifiers in the code should reveal what the code is about.

签名调查14

Signature survey14

在某种程度上查看代码的细节可以揭示其形状。

Looking at the code at some level of detail can reveal its shape.

文档驱动

Documentation driven

首先解释您的目标或最终结果,例如系统将如何使用。

Start by explaining your goal or end result, such as how the system will be used.

滥用活文档(反模式)

Abusing living documentation (anti-pattern)

不要教条地对待实时文档,而是专注于为用户提供价值。

Don’t be dogmatic about living documentation but focus on delivering value for your users.

活文档拖延症

Living documentation procrastination

享受活文档工具的乐趣,避免在生产代码中获得太多乐趣。

Have fun in your living documentation tools to avoid having too much fun in your production code.

可生物降解的文件

Biodegradable documentation

文档的目标应该是让自己变得多余。

The goal of documentation should be to make itself redundant.

设计技巧无处不在

Design skills everywhere

学习并实践良好的设计;它对于您的代码和文档同样有好处。

Learn and practice good design; it’s equally good for your code and for your documentation.

生活建筑

Living Architecture

记录问题

Document the problem

在不解释解决方案试图解决的问题的情况下记录解决方案几乎毫无用处。

It’s almost useless to document a solution without explaining the problem it attempts to solve.

权益驱动架构

Stake-driven architecture

您在该领域面临的最大挑战是理解质量属性还是社会技术方面?

Is your biggest challenge on the domain understanding a quality attribute or socio-technical aspects?

显式质量属性

Explicit quality attributes

朋友不会让朋友猜测系统设计的质量属性。

Friends don’t let friends guess the quality attributes for which a system was designed.

建筑景观

Architecture landscape

将多个文档机制组织成一个一致的整体,以便于导航。

Organize multiple documentation mechanisms into a consistent whole for easier navigation.

决策日志

Decision log

将主要决策保存在决策日志中。

Keep the major decisions in a decision log.

分形架构文档

Fractal architecture documentation

你的系统是由更小的系统组成的;相应地组织您的文档。

Your system is made of smaller systems; organize your documentation accordingly.

建筑法典

Architecture codex

记录您的决策方式可以实现分散决策。

Documenting the way you make decisions enables decentralized decision making.

透明架构

Transparent architecture

建筑适合每个人,只要他们能够访问信息。

Architecture is for everyone, as long as they have access to the information.

建筑真实性检查

Architectural reality check

确保架构的实现符合其意图。

Making sure the implementation of the architecture matches its intent.

测试驱动架构

Test-driven architecture

最终的生活架构是由测试驱动的。

The ultimate living architecture is test driven.

小规模模拟作为文档

Small-scale simulation as documentation

用较小的版本记录一个大型系统。

Document a large system with a smaller version of itself.

系统隐喻15

System metaphor15

每个人(客户、程序员和经理)都共享的具体类比可以帮助您了解系统的工作原理。

A concrete analogy shared by everyone— customers, programmers, and managers—can help you understand how the system works.

引入活文档

Introducing Living Documentation

秘密实验

Undercover experiments

从安全到失败的实验开始,无需太多宣传。

Start with safe-to-fail experiments without much publicity.

边际文件

Marginal documentation

新的做法通常只能应用到新的工作中。

New practices can usually only be applied to new work.

精神上的合规

Compliance in spirit

通过以精神而非文字为目标,活文档方法甚至可以满足最苛刻的合规要求。

A living documentation approach can comply with even the most demanding compliance requirements by aiming for the spirit instead of for the letter.

记录遗留应用程序

Documenting Legacy Applications

僵化的知识

Fossilized knowledge

不应盲目地将遗留系统视为可靠的文档。

Legacy systems should not be considered blindly as reliable documentation.

气泡上下文16

Bubble context16

创建一个独立的空间,您可以在其中不受旧系统的限制进行工作。

Create an isolated space where you can work without the constraint of the legacy system.

叠加结构

Superimposed structure

将理想的结构与现有的不太理想的结构联系起来。

Relate the desirable structure to the existing, less desirable one.

突出显示的结构

Highlighted structure

使叠加结构相对于现有源代码可见。

Make a superimposed structure visible in relation to the existing source code.

外部注释

External annotations

有时您不想仅仅为了向其添加一些知识而接触一个脆弱的系统。

Sometimes you don’t want to touch a fragile system just to add some knowledge to it.

可生物降解转化

Biodegradable transformation

当临时流程完成后,该流程的文档应该消失。

Documentation of a temporary process should disappear when that process is done.

同意格言17

Agree on maxims17

对遗留系统的重大改变是由许多有着共同目标的人进行的;使用格言来分享愿景。

Big changes to legacy systems are made by a number of people who share common objectives; use maxims to share the vision.

强制执行遗留规则

Enforced legacy rule

遗留改造的持续时间比进行改造的人持续的时间更长;自动执行重大决策以保护他们。

Legacy transformations can last longer than the people doing them; automate the enforcement of the big decisions to protect them.

11.埃文斯,埃里克。领域驱动设计:解决软件核心的复杂性。霍博肯:Addison-Wesley Professional,2003。

11.Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. Hoboken: Addison-Wesley Professional, 2003.

12.西蒙·布朗。面向开发人员的软件架构,第 2 卷:可视化、记录和探索您的软件架构https://leanpub.com/visualising-software-architecture

12.Brown, Simon. Software Architecture for Developers, Vol 2: Visualize, document, and explore your software architecture. https://leanpub.com/visualising-software-architecture

13. Brian Marick,“可见工作”: https://web.archive.org/web/20110202132102/ http://visibleworkings.com/

13.Brian Marick, “Visible Workings”: https://web.archive.org/web/20110202132102/http://visibleworkings.com/

14. Ward Cunningham,“签名调查:浏览不熟悉代码的方法”: https://c2.com/doc/SignatureSurvey/

14.Ward Cunningham, “Signature Survey: A Method for Browsing Unfamiliar Code”: https://c2.com/doc/SignatureSurvey/

15.埃文斯,埃里克。领域驱动设计:解决软件核心的复杂性。霍博肯:Addison-Wesley Professional,2003。还有 Beck,Kent。极限编程解释。霍博肯:Addison-Wesley Professional,2000。

15.Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. Hoboken: Addison-Wesley Professional, 2003. also Beck, Kent. Extreme Programming Explained. Hoboken: Addison-Wesley Professional, 2000.

16. Eric Evans,“被遗留系统包围时开始使用 DDD”: http://domainlanguage.com/wp-content/uploads/2016/04/GettingStartedWithDDDWhenSurroundedByLegacySystemsV1.pdf

16.Eric Evans, “Getting Started with DDD when Surrounded by Legacy Systems”: http://domainlanguage.com/wp-content/uploads/2016/04/GettingStartedWithDDDWhenSurroundedByLegacySystemsV1.pdf

17.德迈耶、塞尔日、斯特凡·杜卡斯、奥斯卡·尼尔斯特拉兹。面向对象的重组模式。旧金山:摩根考夫曼出版社,2002 年。

17.Demeyer, Serge, Stéphane Ducasse, Oscar Nierstrasz. Object Oriented Reengineering Patterns. San Francisco: Morgan Kaufmann Publishers, Inc., 2002.

活文档:非常简短的版本

Living Documentation: The Very Short Version

如果您只想花一点时间了解活文档的全部内容,请记住以下重要想法:

If you only want to spend a minute on what living documentation is all about, please remember the following big ideas:

  • 喜欢在各种文档上进行对话和合作。大多数知识已经存在,只是需要挣脱束缚。

  • Favor conversations and working together over every kind of document. Most knowledge is already there and just needs to break free.

  • 大多数知识已经在那里了。您只需要补充缺失的上下文、意图和基本原理即可。

  • Most of the knowledge is there already. You just need to augment it with the missing context, intent, and rationale.

  • 注意变化的频率。

  • Pay attention to the frequency of change.

  • 考虑文档是一种引起人们对系统质量或缺乏质量的关注的方法。

  • Thinking about documentation is a way to draw attention to the quality or lack thereof in a system.

如果这个列表足够清晰,那么您就已经理解了本章的关键信息。

If this list is clear enough, you’ve understood the key message of this chapter.

更好的文档编制方法

Approaches to Better Documentation

有很多方法可以考虑文档主题。这些方法涵盖了整个范围,可以被视为一个循环,遵循从避免文档最大程度的文档,然后再到进一步的进展。再次质疑是否需要文档,并再次循环到更少的文档。您还可以将这个周期视为从轻量级方法到更重量级的方法。

There are many ways to consider the topic of documentation. These approaches cover a full spectrum that can be seen as a cycle that follows a progression from avoiding documentation to documentation to the max and then beyond, to questioning the need for documentation again and looping the cycle to less documentation again. You could also see this cycle as going from lightweight approaches to more heavyweight ones.

这个循环涉及相关知识的变化率(波动性),从稳定的知识到不断变化的知识。

This cycle involves the rate of change (volatility) of the knowledge in question, from stable knowledge to knowledge that changes continuously.

下面描述了以下类别的文档方法,这些方法将在本书中进行讨论:

The following describes the following categories of approaches to documentation, which are discussed throughout this book:

  • 避免文档:最好的文档通常是没有文档,因为除了工作之外,知识不值得任何特别的努力。对话或集体工作的协作是这里的关键。有时,您可以做得更好并改善潜在的情况,而不是通过文档来解决它。例子包括自动化和解决根本问题。

  • Avoiding documentation: The best documentation is often no documentation because the knowledge is not worth any particular effort beyond doing the work. Collaboration with conversation or collective work is key here. Sometimes you can do even better and improve the underlying situation rather than work around it with documentation. Examples include automation and fixing the root issues.

  • 稳定的文档:并非所有知识都会不断变化。当它足够稳定时,文档就会变得更加简单,同时也更加有用。有时,从改变一种知识到一种更稳定的知识只需要一步——这就是你想要利用的机会类型。

  • Stable documentation: Not all knowledge changes all the time. When it’s stable enough, documentation becomes much simpler and much more useful at the same time. Sometimes it just takes one step to go from changing a piece of knowledge to a more stable one—and this is the type of opportunity you want to exploit.

  • 可重构文档:由于现代 IDE 和工具的重构功能,代码、测试、纯文本等提供了持续同步发展的特殊机会。可重构的文档使准确的文档成为可能,并且几乎不需要任何成本。

  • Refactorable documentation: Code, tests, plain text, and so on present particular opportunities to evolve continuously in sync, thanks to the refactoring capabilities of modern IDEs and tools. Refactorable documentation makes possible accurate documentation for little to no cost.

  • 自动化文档:自动化文档是最极客的领域,涉及使用特定的工具以生动的方式自动生成文档,跟随软件构造的变化。自动化文档的一种特殊风格涉及在软件运行时运行的每种方法;这与在构建时起作用的其他方法形成对比。

  • Automating documentation: Automating documentation is the geekiest area, involving using specific tools to produce documentation automatically in a living fashion, following the changes in the software construction. A particular flavor of automating documentation involves every approach that operates at runtime, when the software is running; this is in contrast to other approaches that work at build time.

  • 超越文档:最后,我们到达了超越文档领域,在这里我们有机会质疑一切,并认识到文档主题的好处远远超出了仅仅传输和存储知识。这是我们获得启示并以更批判的方式重新考虑所有其他方法和技术的地方。活文档的这一方面比较抽象,但很重要。通过活文档实践来激发您对工作的注意力,从而提高工作质量。

  • Beyond documentation: Finally, we reach the beyond documentation area, where we have the opportunity to question everything and recognize that the topic of documentation can have benefits well beyond just transferring and storing knowledge. This is where we reach enlightenment and reconsider every other approach and technique in a more critical way. This aspect of living documentation is more abstract, but important. Through living documentation practices that stimulate your attention to your work, the quality of the work can be improved as a side effect.

这些类别构成了本书的主要章节,但顺序相反,从更技术性和更容易掌握到更抽象以及以人为本的考虑。这种顺序意味着章节从不太重要到更重要。

These categories structure the main chapters of this book—but in reverse order to follow a progression from more technical and rather easy-to-grasp to more abstract and people-oriented considerations. This ordering means the chapters progress from the less important to the more important.

在这些方法类别中,本书讨论了一些核心原则,指导您如何有效地进行文档编制。

Across these categories of approaches, this book discusses some core principles that guide you in how to do documentation efficiently.

通往 DDD 的门户

A Gateway to DDD

您可以通过投资实时文档来更接近领域驱动设计。

You can get closer to domain-driven design by investing in living documentation.

活文档可以帮助指导一个或一组团队采用 DDD 实践。它有助于使这些实践更加具体,并将一些注意力集中在生成的工件上。当然,您以 DDD 思维方式工作的方式比生成的工件要重要得多。尽管如此,这些工件至少可以帮助可视化 DDD 的含义,并且它们可以帮助使任何有问题的实践变得可见,并提供有关其完成程度(或未完成程度)的指导。

Living documentation can help guide a team or a set of teams in their adoptions of DDD practices. It helps make these practices more concrete and focuses some attention on the resulting artifacts. Of course, the way you work with the DDD mindset is much more important than the resulting artifacts. Still, the artifacts can at least help visualize what DDD is about, and they can help make visible any problematic practice and provide guidance on how well it’s done (or not).

简而言之,领域驱动设计

Domain-Driven Design in a Nutshell

领域驱动设计是一种解决软件开发核心复杂性的方法。它主要主张重点关注正在考虑的特定业务领域。它提倡编写从字面上表达领域知识的代码,而无需在领域分析和可执行代码之间进行翻译。因此,与许多有关建模的文献相反,它要求直接在用编程语言编写的代码中进行建模。只有能够与领域专家进行频繁且密切的对话,并且每个人都使用相同的普遍语言(业务领域的语言),这才有可能实现。

Domain-driven design is an approach to tackling complexity in the heart of software development. It primarily advocates a sharp focus on the particular business domain being considered. It promotes writing code that expresses the domain knowledge literally, with no translation between the domain analysis and the executable code. As such, it calls for modeling directly in code written in a programming language, in contrast with a lot of literature on modeling. This is possible only if there is the possibility of frequent and close conversations with domain experts, with everyone using the same ubiquitous language—the language of the business domain.

领域驱动设计要求将精力集中在核心领域,即有潜力在竞争中发挥作用的业务领域。因此,DDD 鼓励开发人员不仅交付代码,而且作为业务合作伙伴以建设性的双向关系做出贡献,在这种关系中,开发人员可以深入了解业务并深入了解重要的利害关系。

Domain-driven design calls for focusing the efforts on the core domain, the one business area with the potential to make a difference against the competition. As such, DDD encourages developers not just to deliver code but to contribute as partners with the business in a constructive two-way relationship where the developers grow a deep understanding of the business and gain insights into the important stakes.

领域驱动设计深深植根于 Kent Beck 的《极限编程解释:拥抱变化》。它也建立在模式文献之上,最著名的是 Martin Fowler 的《分析模式:可重用对象模型》和 Rebecca Wirfs-Brock 的《对象设计:角色、责任和协作》,这本书开始了“xDD”命名的实践。

Domain-driven design is deeply rooted in Kent Beck’s Extreme Programming Explained: Embrace Change. It is also built on top of the pattern literature, most notably Martin Fowler’s Analysis Patterns: Reusable Object Models and Rebecca Wirfs-Brock’s Object Design: Roles, Responsibilities, and Collaborations, the book that began the practice of “xDD” naming.

Eric Evans 的《领域驱动设计:解决软件核心的复杂性》还包含许多成功应用 DDD 的模式。最重要的概念之一是有界上下文的概念。有界上下文定义系统中语言可以保持精确且没有歧义的区域。有界上下文是对系统设计的重大贡献;它们将大型、复杂的系统简化并划分为几个更小、更简单的子系统——没有太大的缺点。在团队之间有效地分割系统和工作是相当困难的,有界上下文的概念是一个强大的设计工具,可以帮助解决这个问题。

Eric Evans’s Domain-Driven Design: Tackling Complexity in the Heart of Software also includes numerous patterns for applying DDD successfully. One of the most important concepts is the notion of bounded context. A bounded context defines an area of a system where the language can be kept precise and without ambiguity. Bounded contexts are a major contribution to system design; they simplify and partition large, complicated systems into several smaller and simpler subsystems— without much downside. Splitting systems and work between teams efficiently is quite hard, and the notion of bounded contexts is a powerful design tool to help with this.

由于 Evans 的《领域驱动设计》一书于 2003 年出版,其中的大多数示例都是针对面向对象编程语言中的应用而提出的,但从那时起,人们已经清楚 DDD 也同样适用于函数式编程语言。我经常声称 DDD 提倡函数式编程风格的代码,即使在面向对象的编程语言中也是如此。

Because Evans’s Domain-Driven Design book was published in 2003, most of its  examples were proposed for application in object-oriented programming languages, but it has become clear since then that DDD applies just as well with functional programming languages. I often make the claim that DDD advocates a functional programming style of code even in object-oriented programming languages.

实时文档和领域驱动设计

Living Documentation and Domain-Driven Design

本书重点讨论了 DDD 的几个方面:

This book focuses on several aspects of DDD:

  • 它促进了 DDD 在项目中的使用,特别是通过所选示例。

  • It promotes the use of DDD in a project, in particular through the chosen examples.

  • 它展示了文档如何支持 DDD 的采用以及它如何充当反馈机制来改进您的实践。

  • It shows how documentation can support the adoption of DDD and how it can act as a feedback mechanism to improve your practice.

  • 它本身就是 DDD 在文档和知识管理主题上的应用,以处理该主题的方式。

  • It is in itself an application of DDD on the subject of documentation and knowledge management, in the way this topic is approached.

  • 特别是,活文档的许多实践实际上直接来自 Eric Evans 书中的 DDD 模式。

  • In particular, many of the practices of living documentation are actually directly DDD patterns from Eric Evans’s book.

  • 写这本书的目的是通过文档实践来真正引起人们对设计或缺乏设计的关注,当团队的设计工作做得很差时,这些实践会让人们注意到这一点。

  • The point of writing this book is to actually draw attention to design, or lack of thereof, through documentation practices that make it visible when the team is doing a poor job of design.

这些因素是否使这本书成为一本关于 DDD 的书?我想是这样。作为 DDD 的粉丝,我肯定会喜欢这样的情况。

Do these factors make this book a book on DDD? I think so. As a fan of DDD, I would definitely love this to be the case.

活文档就是让每个决策变得明确,不仅包括代码中的后果,还包括使用代码作为文档媒介的所有表现力来表达(或可能建模)的基本原理、上下文和相关业务利益。

Living documentation is all about making each decision explicit, with not only the consequences in code but also the rationale, the context, and the associated business stakes expressed (or perhaps modeled) using all the expressiveness of the code as a documentation medium.

如果一个项目解决了没有标准解决方案的问题,那么它就是有趣的。该项目必须在探索领域时通过不断学习和大量知识消化来发现如何解决问题。因此,生成的代码会一直发生变化,从小的变化到重大的突破。

A project is interesting if it addresses a problem for which there is no standard solution. The project must discover how to solve the problem through continuous learning and a lot of knowledge crunching while exploring the domain. As a consequence, the resulting code will change all the time, from small changes to major breakthroughs.

“尝试,再试一次”需要易于更改的文档。然而,无论何时,保留花费大量精力学习的宝贵知识都很重要。一旦掌握了知识,您就可以通过编写和重构源代码和其他技术工件将其转化为有价值且可交付的软件。但你需要找到方法在这个过程中保留知识。

“Try, Try Again” requires change-friendly documentation. However, at all times it is important to keep the precious knowledge that took so much effort to learn. Once the knowledge is there, you can turn it into valuable and deliverable software by writing and refactoring source code and other technical artifacts. But you need to find ways to keep the knowledge through this process.

DDD提倡“用代码建模”作为根本解决方案。这个想法是代码本身就是知识的表示。只有当代码不够用时才需要别的东西。战术模式利用了代码是主要媒介的理念,它们指导开发人员如何使用普通的编程语言在实践中使用它。

DDD advocates “modeling with code” as the fundamental solution. The idea is that code itself is a representation of the knowledge. Only when the code is not enough do you need something else. Tactical patterns leverage the idea that code is the primary medium, and they guide developers in how to use it as such in practice, with their ordinary programming language.

因此:您对学习实时文档的投资也是对学习领域驱动设计某些方面的投资。学一个,就可以免费学习另一个的一半!

Therefore: Your investment in learning living documentation is also an investment in learning some aspects of domain-driven design. Learn one, and you learn half of the other for free!

当活文档成为 DDD 的应用时

When Living Documentation Is an Application of DDD

活文档不仅支持 DDD,而且本身就是将 DDD 方法应用于知识整个生命周期管理领域的示例。在许多情况下,活文档是 DDD 的直接应用案例,但名称略有不同。

Living documentation not only supports DDD but is also in itself an example of applying the DDD approach on the domain of managing knowledge throughout its lifecycle. And in many cases, living documentation is a directly applied case of DDD under a slightly different name.

BDD、DDD、XP 和活文档之间相互根源的故事

A Story of Mutual Roots Between BDD, DDD, XP, and Living Documentation

活文档这个术语是由 Gojko Adzic 在《Specification by Example》中引入的,这是一本关于行为驱动开发 (BDD) 的书。BDD 是 Dan North 提出的一种涉及参与软件开发的每个人之间协作的方法,他通过将测试驱动开发 (TDD) 与领域驱动设计的通用语言相结合来引入这一想法。因此,甚至“活文档”这个术语也已经植根于领域驱动设计!

The term living documentation was introduced by Gojko Adzic in Specification by Example, which is a book on behavior-driven development (BDD). BDD is an approach involving collaboration between everyone involved in software development that was proposed by Dan North, who introduced the idea by combining test-driven development (TDD) with the ubiquitous language of domain-driven design. As a consequence, even the term living documentation already has roots in domain-driven design!

考虑到活文档强烈遵循 DDD 的以下原则:

Consider that living documentation strongly adheres to the following tenets of DDD:

  • 代码即模型:代码就是模型(反之亦然),因此您希望在代码中尽可能多地了解模型——根据定义,这就是文档。

  • Code as the model: Code is the model (and vice versa), so you want to have as much knowledge of the model in the code as possible—and this is, by definition, the documentation.

  • 使代码表达所有知识的战术技术:您希望最大限度地利用编程语言可以表达的内容,甚至表达不在运行时执行的知识。

  • Tactical techniques to make the code express all the knowledge: You want to exploit programming languages to the maximum of what they can express, to express even knowledge that is not executed at runtime.

  • 通过 DDD 漩涡不断发展知识:知识处理主要是业务领域专家和开发团队之间的协作问题。通过这个过程,一些最重要的知识会体现在代码中,也许还会体现在其他一些工件中。由于所有知识都会不断发展或随时可能发展,因此任何记录在案的知识都必须拥抱变化,而不会遇到维护成本等障碍。

  • Evolving the knowledge all the time with the DDD whirlpool: Knowledge crunching is primarily a matter of collaboration between business domain experts and the development team. Through this process, some of the most important knowledge becomes embodied into the code—and perhaps into some other artifacts. Because all the knowledge evolves or may evolve at any time, any documented knowledge must embrace change without impediments such as maintenance costs.

  • 明确什么是重要的,什么是不重要的:换句话说,需要关注策划。“专注于核心领域”和“突出核心概念”来自 Evans 的 DDD 书中,但尽管人类的记忆和认知能力有限,但您可以通过管理做更多事情来帮助控制知识。

  • Making clear what’s important and what’s not: In other words, there needs to be a focus on curation. “Focus on the core domain” and “highlight the core concepts” are from Evans’s DDD book, but there’s much more you can do with curation to help keep the knowledge under control despite the limited human memory and cognition capabilities.

  • 关注细节:许多 DDD 模式强调关注细节很重要。决策应该经过深思熟虑,而不是武断,并且应该以具体反馈为指导。动态记录方法必须通过更轻松地记录有意的内容并在整个过程中提供富有洞察力的反馈来鼓励对细节的关注。

  • Attention to detail: Many DDD patterns emphasize that attention to detail is important. Decisions should be deliberate and not arbitrary, and they should be guided by concrete feedback. The living documentation approach has to encourage attention to detail by making it easier to document what’s deliberate and by giving insightful feedback throughout the process.

  • 战略设计和大规模结构: DDD 提供了在战略层面和大规模上处理不断变化的知识的技术,也为更智能的文档提供了机会。

  • Strategic design and large-scale structures: DDD offers techniques to deal with evolving knowledge at the strategic level and on a large scale, providing opportunities for smarter documentation, too.

如果不重写其他书籍的部分内容,就很难提及活文档和领域驱动设计的思想之间的所有对应关系。但需要一些例子来说明这一点(见表1.2)。

It is difficult to mention all the correspondences between the ideas of living documentation and domain-driven design without rewriting parts of other books. But some examples are necessary to make the point (see Table 1.2).

表1-2 Living Documentation与DDD的对应关系

Table 1-2 Correspondence Between Living Documentation and DDD

活的文档模式

Living Documentation Pattern

DDD 模式(来自 Evans 的书或后来的贡献)

DDD Pattern (from Evans’s book or from later contributions)

笔记

Notes

现成的知识;确认参考书目

Ready-made knowledge; acknowledge bibliography

尽可能利用既定的形式主义;读了这本书; 应用分析模式

Draw on established formalisms, when you can; read the book; apply analysis patterns

清楚地声明所有使用的现成知识并引用来源。

Clearly declare all the ready-made knowledge used with references to the sources.

常青文档

Evergreen document

领域愿景声明

Domain vision statement

高级知识是可以写入常青文档的稳定知识的一个很好的例子。

Higher-level knowledge is a great example of stable knowledge that can be written in an evergreen document.

代码作为文档

Code as documentation

模型驱动设计;意图揭示界面;声明式设计;模型驱动设计的构建块(以实现富有表现力的代码)

Model-driven design; intention-revealing interfaces; declarative design; the building blocks of a model-driven design (to enable expressive code)

DDD 是关于用纯代码进行建模,其目的是将所有领域知识体现在代码及其测试中。

DDD is about modeling in plain code, with the purpose of having all the domain knowledge embodied in the code and its test.

生活词汇

Living glossary

无处不在的语言

Ubiquitous language

当代码字面上遵循通用语言时,它就成为该领域术语表的唯一参考。

When the code literally follows the ubiquitous language, it becomes the single reference for the glossary of the domain.

听文档

Listen to the documentation

动手建模师

Hands-on modelers

使用从中提取的实时文档进行代码建模,以实际操作的方式提供有关设计质量的快速反馈。

Modeling in code with living documentation extracted from it gives fast feedback on the quality of the design, in a hands-on fashion.

易于更改的文档

Change-friendlydocumentation

重构以获得更深入的洞察;尝试,再试一次

Refactoring toward deeper insight; try, try again

“拥抱变化”是 XP、DDD 和活文档的永恒主题

“Embracing change” is a constant theme with XP, DDD, and living documentation

策展

Curation

突出显示核心;标记核心;隔离核心;抽象核心

Highlighted core; flagged core; segregated core; abstract core

将特别重要的内容与其他内容分开是 DDD 的关键驱动因素;目标是最好地分配努力和认知注意力。

Segregating what is particularly important from the rest is a key driver in DDD; the goal is to best allocate effort and cognitive attention.

活文档超越了传统文档及其局限性。它详细阐述了 DDD 技术和有关业务领域知识的建议,以及有关设计、基础设施和交付流程的知识,对于项目利益相关者来说,这些知识也是技术领域。领域驱动设计的思想对于指导开发人员如何以战术和战略方式投资知识、应对短期和长期的变化至关重要。因此,当您走活文档路线时,您也在学习领域驱动设计。

Living documentation goes beyond traditional documentation and its limitations. It elaborates on the DDD techniques and advice for knowledge about the business domain and also for knowledge about the design, as well as the infrastructure and delivery process, which are technical domains, too, with respect to the project stakeholders. The ideas from domain-driven design are essential to guiding developers in how to invest in knowledge in a tactical and strategic way, dealing with change in the short term and in the long term as well. Therefore, as you are going the living documentation route, you are learning domain-driven design, too.

概括

Summary

在本章中,您已经看到文档常常由于未受挑战的传统习惯而受到影响。在某种程度上,这是个好消息,因为这意味着有很多机会解构这个主题,以便根据我们今天处理的快节奏和变革友好的项目,从基本原则重新构建它。

Throughout this chapter, you have seen that documentation often suffers because of unchallenged traditional habits. This is good news, in a way, because it means there are many opportunities to deconstruct the topic in order to reconstruct it again from first principles, in the light of the fast-paced and change-friendly projects we deal with today.

活文档就是关注软件制作中涉及的知识。有些知识比其他知识更重要,而且最重要的知识几乎肯定已经存在于项目工件中的某个地方。活文档的目标和乐趣是认识到有价值的知识,它已经在哪里,并确定可能缺少什么以及它改变的频率,以便以最小的费用从中获得最大的利益。换句话说,它是在你的代码库本身中设计一个知识系统,它需要设计技能,就像编码一样!

Living documentation is all about paying attention to the knowledge involved in software making. Some knowledge is more important than other knowledge, and the most important knowledge is almost surely already somewhere in a project’s artifacts. The goal, and the fun, of living documentation is to recognize the valuable knowledge, where it already is, and determine what may be missing and how often it changes in order to best benefit from it with minimal expense. In other words, it is about designing a system of knowledge within your code base itself, and it requires design skills, just like coding!

第2章

Chapter 2

行为驱动开发作为生活规范的一个例子

Behavior-Driven Development as an Example of Living Specifications

记录业务行为怎么样?(因为如你所知,商人永远不会改变主意。)

What about documenting the business behavior? (Because as you know, business people never change their mind.)

行为驱动开发(BDD)是活文档的第一个例子。在《实例化规范》一书中,Gojko Adzic 解释说,许多执行 BDD 的团队表示,最大的好处之一是他们受益于实时文档,该文档解释了应用程序正在做什么,并且可以信任,因为它始终是最新的-日期。

Behavior-driven development (BDD) was the first example of living documentation. In the book Specification by Example, Gojko Adzic explains that many teams doing BDD say that one of the biggest benefits is that they benefit from a living documentation that explains what the application is doing, and that can be trusted because it is always up-to-date.

以下部分结合实时文档快速介绍了 BDD 是什么和不是什么。

The following sections quickly look at what BDD is and what it is not, in relation with living documentation.

BDD 就是对话

BDD Is All About Conversations

如果您认为 BDD 与测试有关,请忘记您认为自己了解的内容。BDD 旨在有效地共享知识。这意味着您无需任何工具即可进行 BDD。首先,BDD 促进了图 2.1中所示的三个(或更多)朋友之间的深入对话。BDD 还依赖于具体场景的使用(必须使用业务领域的语言)来及早发现误解和歧义。

If you think BDD is about testing, forget what you think you know about it. BDD is about sharing knowledge efficiently. This means that you can do BDD without any tools. Before anything else, BDD promotes deep conversations between the three (or more) amigos shown in Figure 2.1. BDD also relies on the use of concrete scenarios— which must use the language of the business domain—to detect misunderstandings and ambiguities early.

图中显示了三个朋友:BIZ、DE 和 Tester。

图2.1 三个朋友

Figure 2.1 The three amigos

自动化 BDD 就是活文档

BDD with Automation Is All About Living Documentation

仅通过对话的 BDD 就提供了很多价值。然而,额外努力设置自动化会带来更多好处。当使用像 Cucumber 这样的工具时,BDD 仍然涉及在每个相关利益相关者之间,特别是在三个朋友之间使用领域语言,关注更高层次的目的,并频繁使用具体示例,也称为场景。这些场景随后成为工具中的测试,同时它们也成为活文档。

BDD with just conversations provides a lot of value. However, going to the additional effort of setting up automation brings even more benefits. When using a tool like Cucumber, BDD still involves the use of a domain language between every stakeholder involved and, in particular, between the three amigos, a focus on the higher-level purpose, and frequent use of concrete examples, also known as scenarios. These scenarios then become tests in the tool, and they become living documentation at the same time.

冗余与协调

Redundancy and Reconciliation

BDD场景描述了应用程序的行为,但应用程序的源代码也描述了这种行为:场景和源代码是相互冗余的,如图2.2所示。

BDD scenarios describe the behavior of an application, but the source code of the application also describes this behavior: The scenarios and source code are redundant with each other, as shown in Figure 2.2.

标题为“冗余”的图显示了描述相同行为的场景和代码。 图中的文字是“如果一个改变而另一个不变怎么办?”

图 2.2 场景和代码都描述了相同的行为

Figure 2.2 Scenarios and code both describe the same behavior

一方面,这种冗余是个好消息:如果处理得当,用纯领域语言表达的场景对于非技术受众(例如永远无法阅读代码的业务人员)来说是可以访问的。然而,这种冗余也是一个问题:如果某些场景或部分代码独立演化,那么你就会遇到两个问题:你必须确定是否信任这些场景或代码,并且(一个更大的问题),你必须以某种方式知道场景和代码不同步。

On one hand, this redundancy is good news: Scenarios expressed in pure domain language, if done properly, are accessible to nontechnical audiences such as business people who could never read code. However, this redundancy is also a problem: If some scenarios or parts of the code evolve independently, then you have two problems: You must determine whether to trust the scenarios or the code, and (a bigger problem), you must in some way know that the scenarios and the code are not in sync.

这就是需要协调机制的地方。对于 BDD,您可以使用 Cucumber 或 SpecFlow 等测试和工具。这些工具的作用就像是两个冗余知识之间的 Roberval 平衡,如图2.3所示。

This is where a reconciliation mechanism is needed. In the case of BDD, you can use tests and tools like Cucumber or SpecFlow. These tools act like a Roberval balance between both redundant pieces of knowledge, as pictured in Figure 2.3.

标题为 Cucumber Specflow 的图显示了场景和由工具检查的代码是否描述了相同的行为。

图 2.3 工具定期检查场景和代码是否描述相同的行为

Figure 2.3 Tools check regularly that the scenarios and the code describe the same behavior

这些工具以纯文本形式解析场景,并使用开发人员提供的一些粘合代码来驱动实际代码。调用实际代码时,将提取场景的“Given”和“When”部分中的金额、日期和其他值并将其作为参数传递。另一方面,从场景的“Then”部分提取的值用于断言,以根据代码匹配的结果检查场景中的预期内容。

These tools parse the scenarios in plain text and use some glue code provided by the developers to drive the actual code. The amounts, dates, and other values in the “Given” and “When” sections of the scenarios are extracted and passed as parameters when calling the actual code. The values extracted from the “Then” sections of the scenarios, on the other hand, are used for the assertions, to check what’s expected in the scenario, based on the result from the code matches.

这些工具将场景转变为自动化测试。好处是这些测试还提供了一种方法来检测场景和代码何时不再同步。这是协调机制的一个示例,这是一种确保冗余信息集始终匹配的方法。

The tools turn scenarios into automated tests. The nice thing is that these tests also provide a way to detect when the scenarios and the code are no longer in sync. This is an example of a reconciliation mechanism, a means to ensure that redundant sets of information always match.

文件中场景的剖析

The Anatomy of Scenarios in a File

当使用 Cucumber 或 SpecFlow 等工具将场景自动化到测试中时,您可以创建名为特征文件的文件。这些文件是存储在源代码管理中的纯文本文件,就像代码一样。通常它们存储在测试附近或作为 Maven 测试资源。这意味着它们像代码一样进行版本控制,并且很容易区分。

When using a tool like Cucumber or SpecFlow to automate scenarios into tests, you create files called feature files. These files are plain-text files that are stored in the source control, just like code. Usually they are stored near the tests or as Maven test resources. This means they are versioned like the code, and they are easy to diff.

让我们仔细看看功能文件。

Let’s take a closer look at a feature file.

功能文件的意图

The Intent of a Feature File

功能文件必须以描述文件中所有场景的意图的叙述开始。它通常遵循“为了……”的模式。作为一个…。我想要……”以“为了”开头可以帮助您专注于最重要的事情:您正在寻找的价值。

A feature file must start with a narrative that describes the intent of all the scenarios in the file. It usually follows the pattern “In order to…. As a…. I want.…” Starting with “In order to” helps you focus on the most important thing: the value you’re looking for.

以下是关于在包裹递送车队管理背景下检测潜在欺诈的应用程序的叙述示例:

Here’s an example of a narrative for an application about detection of potential frauds in the context of fleet management for parcel delivery:

1 特征:加油卡交易异常
2 为了检测潜在的加油卡异常行为
司机
3 作为车队经理
4 我想自动检测所有加油卡的异常情况
交易
1  Feature: Fuel Card Transactions anomalies
2  In order to detect potential fuel card abnormal behavior by
drivers
3  As a fleet manager
4  I want to automatically detect anomalies in all fuel card
transactions

请注意,这些工具仅将叙述视为文本;除了将其包含在报告中之外,他们不会对它做任何事情,因为他们承认它很重要。

Note that the tools just consider the narrative as text; they don’t do anything with it except include it in the reports because they acknowledge that it’s important.

特征文件场景

Feature File Scenarios

功能文件的其余部分通常列出与相应功能相关的所有场景。每个场景都有一个标题,并且场景几乎总是遵循“鉴于……”。什么时候…。然后……”模式。

The rest of the feature file usually lists all the scenarios that are relevant for the corresponding feature. Each scenario has a title, and scenarios almost always follow the “Given…. When…. Then…” pattern.

以下是在包裹递送车队管理背景下检测潜在欺诈的应用程序的众多具体场景之一的示例:

Here’s an example of one of the many concrete scenarios for an application on detection of potential frauds, in the context of fleet management for parcel delivery:

1 场景:油量多于车辆的油量交易
坦克可以容纳
2 假设车辆 23 的油箱尺寸为 48L
3 加油卡上报告 52L 交易时
4 与车辆 23 相关
5 然后出现异常“52L的燃油交易超过油箱
据报道尺寸为48L”
1  Scenario: Fuel transaction with more fuel than the vehicle
tank can hold
2  Given that the tank size of the vehicle 23 is 48L
3  When a transaction is reported for 52L on the fuel card
4  associated with vehicle 23
5  Then an anomaly "The fuel transaction of 52L exceeds the tank
size of 48L" is reported

在一个功能文件中,通常有 3 到 15 个场景,描述快乐路径、其变体以及最重要的情况。

Within one feature file there are typically between 3 and 15 scenarios, describing the happy path, its variants, and the most important situations.

还有许多其他方法可以描述场景,例如使用大纲格式,并且还有一些方法可以分解场景与背景场景之间的共同假设。(有关大纲格式和背景场景的更多信息,请参阅https://docs.cucumber.io/gherkin/reference/。)

There are a number of other ways to describe scenarios, such as by using the outline format, and there are also ways to factor out common assumptions between scenarios with background scenarios. (For more information on outline format and background scenarios, see for example https://docs.cucumber.io/gherkin/reference/.)

规格详情

Specification Details

在很多情况下,仅场景就足以描述预期的行为,但在一些丰富的业务领域,例如会计或金融,场景肯定是不够的。在这种情况下,您还需要抽象的规则和公式。

There are many cases in which scenarios alone are enough to describe the expected behavior, but in some rich business domains, such as accounting or finance, scenarios are definitely not enough. In such cases, you also need abstract rules and formulas.

您无需将所有这些附加知识放入 Word 文档或 wiki 中,而是可以直接将其嵌入到相关功能文件中的意图和场景列表之间。这是一个示例,来自与之前相同的功能文件:

Rather than putting all this additional knowledge in a Word document or in a wiki, you can directly embed it within the related feature file, between the intent and the list of scenarios. Here’s an example, from the same feature file as before:

1 特征:加油卡交易异常
2 为了检测潜在的加油卡异常行为
司机
3 作为车队经理
4 我想自动检测所有加油卡的异常情况
交易
5
6 说明:
7 监控检测到以下异常情况:
8 * 燃油泄漏:只要容量 > 1 + 公差,
9 其中容量=交易燃油数量/车辆油箱尺寸
10 * 交易距离车辆太远:每当距离
车辆 > 阈值,
11 其中到车辆的距离 = 地理距离(车辆
坐标,气体
12个站坐标),
13、其中车辆坐标由GPS提供
14 跟踪(车辆、时间戳)、
15 加油站坐标由以下提供:
16对其邮寄地址进行地理编码。
17 号
18 场景:燃料交易无异常
19 当加油卡上报告交易时
20 .../// 这里有更多场景
1  Feature: Fuel Card Transactions anomalies
2  In order to detect potential fuel card abnormal behavior by
drivers
3  As a fleet manager
4  I want to automatically detect anomalies in all fuel card
transactions
5
6  Description:
7  The monitoring detects the following anomalies:
8  * Fuel leakage: whenever capacity > 1 + tolerance,
9  where capacity = transaction fuel quantity / vehicle tank size
10 * Transaction too far from the vehicle: whenever distance to
vehicle > threshold,
11 where distance to vehicle = geo-distance (vehicle
coordinates, gas
12 station coordinates),
13 and where the vehicle coordinates are provided by the GPS
14 Tracking by (vehicle, timestamp),
15 and where the gas station coordinates are provided by
16 geocoding its post address.
17
18 Scenario: Fuel transaction with no anomaly
19 When a transaction is reported on the fuel card
20 .../// more scenarios here

这些规范细节只是作为自由文本的注释;工具完全忽略它。然而,把它放在那里的目的是与相应的场景共存。每当您更改场景或细节时,您更有可能更新规范细节,因为它们非常接近。正如我们所说,“眼不见心不烦”。但并不能保证这样做。

These specification details are just comments as free text though; the tools completely ignore it. However, the point of putting it there is to have co-located with the corresponding scenarios. Whenever you change the scenarios or the details, you are more likely to update the specification details because they are so close. As we say, “out of sight, out of mind.” But there is no guarantee to do so.

特征文件中的标签

Tags in Feature Files

功能文件中的最后一个重要组成部分是添加标签的能力。每个场景都可以有标签,如下所示:

The last significant ingredient in feature files is the ability to add tags. Each scenario can have tags, like the following:

1 @acceptance-criteria @specs @wip @fixedvenue @interests
2 场景:一年内的双年复利
3 假设本金为 1000 美元
4 ...//
1  @acceptance-criteria @specs @wip @fixedincome @interests
2  Scenario: Bi-annual compound interests over one year
3  Given a principal of USD 1000
4  ...//

标签是文档。有些标签描述了项目管理知识,例如@wip,它代表正在进行的工作并表示该场景当前正在开发。其他类似的标签甚至可能会命名参与开发的人员(例如@bob、@team-red)或提及冲刺(例如@sprint-23)或其目标(例如@learn-about-reporting-需要)。这些标签是临时的,任务完成后将被删除。

Tags are documentation. Some tags describe project management knowledge, such as @wip, which stands for work in progress and signals that this scenario is currently being developed. Other similar tags may even name who’s involved in the development (for example, @bob, @team-red) or mention the sprint (for example, @sprint-23) or its goal (for example, @learn-about-reporting-needs). These tags are temporary and are deleted when the tasks are all done.

有些标签描述了场景的重要性。例如,@acceptance-criteria 表示此场景是少数用户接受标准的一部分。其他类似的标签可能有助于场景的策划,例如 @happy-path、@nominal、@variant、@negative、@exception 和 @core。

Some tags describe how important the scenario is. For example, @acceptance-criteria indicates that this scenario is part of the few user acceptance criteria. Other similar tags may help with curation of scenarios, such as @happy-path, @nominal, @variant, @negative, @exception, and @core.

最后,一些标签描述了业务领域的类别和概念。例如,在刚刚显示的示例中,标签@fixedvenue和@interests描述了该场景与固定收益和利息金融领域相关。

Finally, some tags describe categories and concepts from the business domain. For example, in the example just shown, the tags @fixedincome and @interests describe that this scenario is relevant to the fixed income and interest financial areas.

标签也应该被记录下来。例如,并置文本文件可以列出所有有效标签以及每个标签的文本描述。为了确保功能文件中使用的每个标签都记录在那里,我的同事 Arnauld Loyer 喜欢添加一个单独的单元测试作为另一种协调机制。

Tags should be documented, too. For example, a collocated text file can list all the valid tags, along with a text description for each. And to make sure that every tag used in the feature files is documented there, my colleague Arnauld Loyer likes to add a separate unit test as yet another reconciliation mechanism.

组织功能文件

当功能文件数量增加时,有必要将它们组织到文件夹中。您使用的组织也是传播知识的一种方式;文件夹可以讲述一个故事。

When the number of feature files grows, it’s necessary to organize them into folders. The organization you use is also a way to convey knowledge; the folders can tell a story.

当业务领域是最重要的事情时,我建议按功能区域组织文件夹以显示整体业务情况。例如,您可能有以下文件夹:

When the business domain is the most important thing, I recommend organizing the folders by functional areas to show the overall business picture. For example, you might have the following folders:

  • 会计

  • Accounting

  • 举报规则

  • Reporting Rules

  • 折扣

  • Discounts

  • 特别优惠

  • Special Offers

如果您有任何其他内容(例如文本和图片),也可以将其包含在同一文件夹中,以便它尽可能接近相应的场景。

If you have any additional content as text and pictures, you can also include it in the same folders, so that it stays as close as possible to the corresponding scenarios.

在《实例规范》一书中,Gojko Adzic 列出了将故事组织到文件夹中的三种方法:

In the book Specification by Example, Gojko Adzic lists three ways to organize stories into folders:

  • 按功能区划分

  • By functional areas

  • 沿着 UI 导航路线(记录用户界面时)

  • Along UI navigation routes (when documenting user interfaces)

  • 沿着业务流程(当需要端到端用例可追溯性时)

  • Along business processes (when end-to-end use case traceability is required)

通过这种方法,文件夹实际上代表了业务文档的章节(正如您在本章后面的示例中看到的那样)。

With this approach, the folders literally represent the chapters of your business documentation (as you can see in the example later in this chapter).

场景作为互动活文档

Scenarios as Interactive Living Documentation

这些场景构成了活文档的基础。更好的是,该文档通常是交互式的,作为生成的交互式网站。例如,如果您使用 Pickles for SpecFlow,每次构建期间都会生成一个特定的单页网站(见图2.4)。该网站会显示按章节组织的导航窗格,前提是您的文件夹代表功能章节。它显示所有场景以及测试结果及其统计数据。这非常强大——比您见过的任何纸质文档都要强大得多。

The scenarios form the basis for living documentation. Even better, this documentation is typically interactive, as a generated interactive website. For example, if you use Pickles for SpecFlow, a specific one-page website is generated during each build (see Figure 2.4). The website shows a navigation pane that is organized by chapter, provided that your folders represent functional chapters. It displays all scenarios, together with the test results and their statistics. This is quite powerful—much more so than any paper documentation you’ve ever seen.

下图显示了使用 Pickles 生成的交互式文档网站。

图2.4 生成的交互式文档网站,带有Pickles

Figure 2.4 Generated interactive documentation website, with Pickles

Pickles 中的内置搜索引擎允许通过关键字或标签即时访问任何场景。这是标签的第二个强大作用:它们使搜索更加高效和准确。

A built-in search engine in Pickles allows instant access to any scenario by keyword or by tag. This is the second powerful effect of tags: They make searching more efficient and accurate.

无聊的纸质文档中的场景

Scenarios in Boring Paper Documents

像上一节所示的交互式网站对于团队来说很方便,可以快速访问业务行为知识。然而,在某些情况下,例如当存在强制性合规性要求时,您必须提供无聊的纸质文档(有些人称之为“BPD”)。

An interactive website like the one shown in the preceding section is convenient for a team, providing fast access to the business behavior knowledge. However, in some cases, such as when there are mandatory compliance requirements, you must provide a boring paper document (a “BPD,” as some call it).

有用于创建这些文档的工具。其中一个是由我的 Arolla 同事 Arnauld Loyer (@aloyer) 开发的,称为 Tzatziki 1,因为它是一种黄瓜酱。它从功能文件中导出一个漂亮的 PDF 文档。它更进一步,还包括与功能文件一起存储到文档中的 Markdown 文件和图像。因此,它有助于在每个功能区域章节的开头创建很好的解释。

There are tools for creating these documents. One of them, which was developed by my Arolla colleague Arnauld Loyer (@aloyer), is called Tzatziki1, because it’s a Cucumber sauce. It exports a beautiful PDF document out of the feature files. It goes a bit further, also including Markdown files and images that are stored alongside the feature files into the document. It therefore helps create nice explanations at the beginning of each functional area chapter.

1. Tzatziki, https://github.com/Arnauld/tzatziki

1.Tzatziki, https://github.com/Arnauld/tzatziki

笔记

Note

如果您的上下文中缺少所需的工具,您应该在现有工具的基础上创建它或将其作为现有工具的派生。天空是极限。可以在黑客马拉松日或闲暇时间将自定义工具或工具扩展创建为有趣的项目;它们不一定是由供应商或其他人制作的。

If the tool you need in your context is missing, you should create it on top of or as a derivation of existing tools. The sky’s the limit. Custom tools or extensions of tools can be created as fun projects, on hackathon days, or during slack time; they needn’t be made by vendors or other people.

BDD 是活文档的一个很好的例子:它不是需要完成的额外工作,而是正确工作的一部分。由于充当协调机制的工具,它始终保持同步。如果源代码中的功能文件还不够,生成的网站将说明文档如何变得有用、交互式、可搜索和组织良好。

BDD is a great example of living documentation: It’s not additional work to be done but is part of doing work properly. It’s always in sync, thanks to the tools that act as reconciliation mechanisms. And if the feature files in the source code are not enough, the generated website illustrates how documentation can be useful, interactive, searchable, and well organized.

特征文件示例

A Feature File Example

本节提供了金融业务领域中虚构但现实的特征文件的完整示例。为了简单起见,此示例仅包含一个概要场景以及相应的数据表。它说明了使用 Cucumber、SpecFlow 和等效工具的另一种方式。针对表的每一行评估场景。这是完整的功能文件示例:

This section provides a full example of a fictitious yet realistic feature file in the business domain of finance. To keep it short, this example contains only one outline scenario, along with a corresponding data table. It illustrates another style of using Cucumber, SpecFlow, and equivalent tools. The scenario is evaluated for each line of the table. Here is the complete feature file example:

1 功能:计算本金复利
2 为了管理公司资金
3 作为财务人员
4 我想计算本金的复利
我的账户
5
6 说明:
7 复利是指银行同时支付利息
   本金(原始金额)和利息
   账户已经赚了。
8
9 要计算复利,请使用以下公式。
10
11 式中,A代表账户最终金额
   t年后以利率“r”复利“n”次
   起始金额“p”。
12
13
14
15 A = P*(1+(r/n))^n*t
16
17 号
18 场景:一年内的双年复利
19 给定本金 1000 美元
20 利息每半年复利一次,利率为 5%
21 当计算期间恰好为 1 年时
22 那么账户里的金额是 USD 1053.63
23
24 场景:一年内的季度复利
25 //...概述场景
26
27 示例:
28
29 | 29 大会 | 率 | 时间 | 金额 | 备注 |
30 |------------------------------------------------ ----------- |
31 | 线性 | 0.05 | 0.05 2 | 0.100000 | (1+rt)-1 |
32 | 32 复合 | 0.05 | 0.05 2 | 0.102500 | (1+r)^t-1 | (1+r)^t-1 |
33 | 33 折扣 | 0.05 | 0.05 2 | -0.100000 | -0.100000 (1 - rt)-1 |
34 | 34 连续| 0.05 | 0.05 2 | 0.105171 | 0.105171 (e^rt)-1(罕见)|
35 | 35 无 | 0.05 | 0.05 2 | 0 | 0 |
36 |------------------------------------------------ ----------- |
1  Feature: Calculate compound interests on a principal
2  In order to manage the company money
3  As a finance officer
4  I want to calculate the compound interests on a principal on
my account
5
6  Description:
7  Compound interest is when the bank pays interest on both the
   principal (the original amount of money) and the interest an
   account has already earned.
8
9  To calculate compound interest use the formula below.
10
11 In the formula, A represents the final amount in the account
   after t years compounded 'n' times at interest rate 'r' with
   starting amount 'p'.
12
13
14
15  A = P*(1+(r/n))^n*t
16
17
18 Scenario: Bi-annual compound interests over one year
19 Given a principal of USD 1000
20 And interests are compounded bi-annually at a rate of 5%
21 When the calculation period lasts exactly 1 year
22 Then the amount of money in the account is USD 1053.63
23
24 Scenario: Quarterly compound interests over one year
25 //... outline scenario
26
27 Examples:
28
29 | convention   | rate | time | amount    | remarks           |
30 |----------------------------------------------------------- |
31 | LINEAR       | 0.05 | 2    | 0.100000  | (1+rt)-1          |
32 | COMPOUND     | 0.05 | 2    | 0.102500  | (1+r)^t-1         |
33 | DISCOUNT     | 0.05 | 2    | -0.100000 | (1 - rt)-1        |
34 | CONTINUOUS   | 0.05 | 2    | 0.105171  | (e^rt)-1 (rare)   |
35 | NONE         | 0.05 | 2    | 0         | 0                 |
36 |----------------------------------------------------------- |

在工具的支持下,所有业务场景同时成为自动化测试和活文档。这些场景只是功能文件中的纯文本。为了弥合场景中的文本和实际生产代码之间的差距,您创建了一组步骤。每个步骤都在特定的文本句子上触发,通过正则表达式进行匹配,并调用生产代码。文本句子可能具有被解析并用于以多种不同方式调用生产代码的参数。这是一个例子:

With the support of tools, all business scenarios become automated tests and living documentation at the same time. The scenarios are just plain text in the feature files. To bridge the gap between the text in the scenarios and the actual production code, you create a little set of steps. Each step is triggered on a particular text  sentence, matched by regular expressions, and calls the production code. The text sentence may have parameters that are parsed and used to call the production code in many different ways. Here is an example:

1 例如:
2 鉴于增值税税率为 9.90%
3 当我购买不含增值税价格为 25 欧元的书时
4 然后我必须支付 2.49 欧元的含增值税价格
1  For example:
2  Given the VAT rate is 9.90%
3  When I but a book at an ex-VAT price of EUR 25
4  Then I have to pay an inc-VAT price of EUR 2.49

要自动化此场景,您需要为场景中使用的每一行定义所谓的步骤。例如,您可以定义任何这样的句子:

To automate this scenario, you need to define what is called a step for each line used within scenarios. For example, you can define that any sentence like this:

1“当我购买一本书,不含增值税价格为欧元 <exVATPrice>”
1  "When I but a book at an ex-VAT price of EUR <exVATPrice>"

会触发以下粘合代码:

Would trigger the following glue code:

1 本书(数量 exVATPrice)
2 服务 = LookupOrderService();
3 Service.sendOrder(exVATPrice);
1  Book(number exVATPrice)
2  Service = LookupOrderService();
3  Service.sendOrder(exVATPrice);

在此代码片段中,工具(Cucumber 或 SpecFlow)将变量exVAT-Price传递给粘合代码;该变量的值是从场景中的句子中自动提取的。例如,在上述场景中,exVAT-Price 的值为 25。

In this code snippet, the tool (Cucumber or SpecFlow) passes the variable exVAT-Price to the glue code; the value of this variable is automatically extracted from the sentence in a scenario. For example, in the preceding scenario, the value of exVAT-Price would be 25.

使用这种机制,场景成为由场景及其声明的值驱动的自动化测试。如果在不修改代码的情况下改变场景中价格的舍入模式,测试将会失败。如果在代码中更改价格的舍入模式而不更改场景,测试也会失败。这是一种协调机制,用于发出冗余双方之间不一致的信号。

Using this mechanism, the scenarios become automated tests that are driven by the scenarios and the values they declare. If you change the rounding mode of the price in the scenario without changing the code, the test will fail. If you change the rounding mode of the price in the code without changing the scenario, the test will fail, too. This is a reconciliation mechanism to signal inconsistencies between the sides of the redundancy.

各个方面的活文档的典型案例

A Canonical Case of Living Documentation in Every Aspect

BDD 表明,通过更仔细地进行规范工作,可以获得始终与代码同步的准确文档。BDD 是活文档的典型案例,活文档的所有核心原则都已经存在于 BDD 中:

BDD has shown that it is possible to have accurate documentation that is always in sync with the code by doing the specification work more carefully. BDD is a canonical case of living documentation, and all the core principles of living documentation are already present in BDD:

  • 协作: BDD 的主要工具是人与人之间的交谈,确保三个朋友(或更多)中的每个角色都在场。

  • Collaborative: The primary tool of BDD is talking among people, making sure that each role out of the three amigos (or more) is present.

  • 省力:围绕具体示例的对话对于就构建内容达成一致非常有用,并且通过一些额外的工作,它们将成为自动化测试和活生生的文档:一项活动,多种好处。

  • Low-effort: The conversations around concrete examples are useful for agreeing on what to build, and with some additional work they become automated tests and a living documentation: one activity, multiple benefits.

  • 可靠,得益于协调机制:由于业务行为是在文本场景和实现代码中描述的,因此 Cucumber 和 SpecFlow 等工具可确保场景和代码始终保持同步(或至少在不同步时显示出来) 。每当存在知识重复时,这是必要的。

  • Reliable, thanks to a reconciliation mechanism: Because the business behaviors are described both in text scenarios and in implementation code, tools like Cucumber and SpecFlow ensure that the scenarios and code remain always in sync (or at least show when they go out of sync). This is necessary whenever there is duplication of knowledge.

  • 富有洞察力:对话提供反馈,编写和自动化场景也能提供反馈。例如,如果场景太长或尴尬,它可能会建议寻找缺失的隐含概念,以使场景更短、更简单。

  • Insightful: The conversations provide feedback, as do writing and automating the scenarios. For example, if a scenario is too long or awkward, it may suggest looking for the missing implicit concepts that would make the scenario shorter and simpler.

它还说明了本书后面描述的其他想法:

It also illustrates other ideas described later in this book:

  • 目标受众:所有这些工作都是针对包括业务人员在内的受众,因此在讨论业务需求时重点关注清晰的非技术语言。

  • Targeted audience: All this work is targeted for an audience that includes business people, hence the focus on clear, nontechnical language when discussing business requirements.

  • 想法沉淀:对话往往就足够了,并不是所有的事情都需要写下来。只有最重要的场景、关键的场景,才需要编写用于归档或自动化。

  • Idea sedimentation: Conversations are often enough, and not everything needs to be written down. Only the most important scenarios, the key scenarios, need to be written for archiving or automation.

  • 纯文本文档:纯文本对于管理更改的内容以及与源代码管理中的源代码一起使用非常方便。

  • Plain-text documents: Plain text is very convenient for managing stuff that changes and for living alongside the source code in source control.

  • 可访问的已发布快照:并非每个人都拥有或希望访问源代码管理以读取场景。Pickles 和 Tzatziki 等工具提供了一种解决方案,将当前时间点的所有场景的快照导出为交互式网站或可打印的 PDF 文档。

  • Accessible published snapshot: Not everyone has or wants access to the source control in order to read the scenarios. Tools like Pickles and Tzatziki offer a solution, exporting a snapshot of all the scenarios at a current point in time as an interactive website or as a PDF document that can be printed.

既然您已经将 BDD 视为活文档的典型案例,那么您就可以转向可以应用活文档的其他环境了。活文档并不像 BDD 那样仅限于业务行为的描述;它可以在软件开发项目的许多其他方面为您提供帮助,甚至可能在软件开发之外。

Now that you’ve seen BDD as the canonical case of living documentation, you’re ready to move on to other contexts where you can apply living documentation. Living documentation is not restricted to the description of business behaviors, as is BDD; it can help you in many other aspects of software development projects—and perhaps even outside software development.

更进一步:充分利用您的活文档

Going Further: Getting the Best of Your Living Documentation

描述业务场景的功能文件是有效收集丰富领域知识的好地方。

Feature files describing business scenarios are a great place to gather rich domain knowledge in an efficient way.

大多数支持团队执行 BDD 的工具都了解 Gherkin 语法。他们希望功能文件遵循固定格式,如下所示:

Most tools that support teams in doing BDD understand the Gherkin syntax. They expect feature files to follow a fixed format, as shown here:

1 功能:功能名称
2
3 为了... 作为... 我想要...
4
5场景:第一个场景的名称
6 鉴于...
7 当...
8 然后...
9
10 场景:第二个场景的名称
11 ...
1  Feature: Name of the feature
2
3  In order to... As a... I want...
4
5 Scenario: name of the first scenario
6  Given...
7  When...
8  Then...
9
10 Scenario: name of the second scenario
11 ...

随着时间的推移,金融或保险等丰富领域的团队意识到他们需要更多的文档,而不仅仅是顶部的意图和底部的具体场景。因此,他们开始在中间区域添加对其业务案例的附加描述,而工具会忽略这一点。像 Pickles 这样的工具可以从适应这种用途的功能文件中生成实时文档,并开始支持所谓的“描述区域”的 Markdown 格式:

Over time, teams in rich domains like finance or insurance realized that they needed more documentation than just the intent at the top and the concrete scenarios at the bottom. As a result, they started putting additional description of their business case in the middle area, and this is ignored by the tools. Tools like Pickles that generate living documentation out of the feature files adapted to this use and started to support Markdown formatting for what became called “the description area”:

1 特点:投资现值
2
3 为了计算投资的盈亏平衡点
机会
4 作为投资经理
5 我想计算未来现金金额的现值
6
7
8 说明
9 ===========
10
11 我们需要找到给定未来的现值 *PV*
现金 \
12 金额 *FV*。公式可以表示为:
13
14 - 使用负指数表示法:
15
16 PV = FV * (1 + i)^(-n)
17 号
18 - 或等效形式:
19
20 PV = FV * (1 / (1 + i)^n)
21
22 示例
23 --------
24
25 例如,n = 2,i = 8%
26    光伏?实际价值 = 100 美元
27 | 27 | |
28 --------------------------------------------------> t(年)
29 0 1 2
30
32
33 场景:单笔现金金额的现值
34 假设 2 年内未来现金金额为 100 美元
35 利率为 8%
36 当我们计算它的现值时
37 那么它的现值为 $85.73
1  Feature: Investment Present Value
2
3  In order to calculate the breakeven point of the investment
opportunity
4  As an investment manager
5  I want to calculate the present value of future cash amounts
6
7
8  Description
9  ===========
10
11 We need to find the present value *PV* of the given future
cash \
12 amount *FV*. The formula for that can be expressed as:
13
14 - Using the negative exponent notation:
15
16         PV = FV * (1 + i)^(-n)
17
18 - Or in the equivalent form:
19
20         PV = FV * (1 / (1 + i)^n)
21
22 Example
23 -------
24
25    For example, n = 2, i = 8%
26    PV?                       FV = $100
27    |              |           |
28    ---------------------------------------> t (years)
29    0              1           2
30
32
33 Scenario: Present Value of a single cash amount
34  Given a future cash amount of 100$ in 2 years
35  And an interest rate of 8%
36  When we calculate its present value
37  Then its present value is $85.73

该文档将在实时文档网站中呈现为一个漂亮的文档,名为“特征:投资现值”。

This documentation will be rendered in the living documentation website as a pretty document called “Feature: Investment Present Value.”

此示例说明了功能文件如何提供直接在源代码管理中的同一位置收集大量文档的机会。请注意,文件中间包含文本、公式和 ASCII 图表的描述性区域并不是真正存在的。它只是与场景位于同一位置;如果我们更改场景,您可能还需要更新附近的描述,但不能保证。

This example illustrates how feature files provide the opportunity to gather a lot of documentation in the same place, directly within the source control. Note that this descriptive area with text, formulas, and ASCII diagrams in the middle of the file is not really living; it’s is just co-located with the scenarios; if we change the scenarios, you’re likely to also need to update the description nearby, but there is no guarantee.

最好的策略是将不经常变化的知识放在描述部分,并将易变的部分保留在具体场景中。实现此目的的一种方法是澄清描述使用样本编号,而不是在任何时间点都必须用于业务流程配置的编号。

The best strategy would be to put knowledge that does not change very often in the description section and to keep the volatile parts within the concrete scenarios. One way to do this is to clarify that the description uses sample numbers, not the numbers that are necessarily used for the configuration of the business process at any point in time.

Pickle、2 Relish、3和 Tzatzikinow 等工具现在可以理解 Markdown 描述,甚至可以理解位于功能文件旁边的纯 Markdown 文件。这使得为​​领域文档采用集成且一致的方法变得容易。正如金融监管机构所期望的那样,Tzatziki 可以从所有这些知识中导出 PDF。

Tools like Pickle,2 Relish,3 and Tzatzikinow understand Markdown descriptions and even plain Markdown files located next to the feature files. This makes it easy to have an integrated and consistent approach for the domain documentation. And Tzatziki can export a PDF from all this knowledge, as expected by the regulators in finance.

2.Pickle http://www.picklesdoc.com

2.Pickle, http://www.picklesdoc.com

3.津味, http://www.relishapp.com

3.Relish, http://www.relishapp.com

基于属性的测试和 BDD

Property-Based Testing and BDD

要求通常作为属性自然而然地出现(例如,“支付和收到的所有金额的总和必须始终为零”或“没有人可以同时成为律师和法官”)。在进行 BDD 或 TDD 时,必须将这些通用属性阐明为具体的具体示例,这将有助于发现问题并逐步构建代码。

Requirements often come naturally as properties (for example, “The sum of all amounts paid and received must be zero at all times” or “Nobody can ever be a lawyer and a judge at once”). When doing BDD or TDD, you must clarify these general properties into specific concrete examples, which will help with finding issues and building code incrementally.

跟踪一般属性以了解其文档价值是个好主意。您通常在功能文件中以纯文本注释的形式执行此操作,如本章前面所述。但碰巧的是,基于属性的测试技术正是针对随机生成的样本来运用这些属性。这是通过基于属性的测试框架执行的,该框架一遍又一遍地运行相同的测试,并使用样本生成器生成的输入。规范的框架是 Haskell 中的 QuickCheck,现在大多数其他编程语言中都有类似的工具。

It’s a good idea to keep track of the general properties for their documentation value. You usually do that as plain text comments in the feature file, as described earlier in this chapter. But it happens that the technique of property-based testing is precisely about exercising these properties against randomly generated samples. This is performed with a property-based testing framework that runs the same test over and over, with inputs generated from generators of samples. The canonical framework is QuickCheck in Haskell, and there are now similar tools in most other programming languages.

将基于属性的测试集成到功能文件中最终也使常规属性也可执行。在实践中,只需添加描述通用属性的特殊场景并调用下面基于属性的测试框架,如下所示:

Integrating property-based testing into your feature files eventually makes the general properties executable, too. In practice, it’s a matter of adding special scenarios describing the general property and invoking the property-based testing framework underneath, as shown here:

1 场景:所有兑换的现金金额之和必须为零
对于衍生品
2
3 给定任何衍生金融工具
4 以及其生命周期内的随机日期
5 当我们在该日产生相关现金流量时
付款人和\
6 接收器
7 那么付款人和接收人的现金流量之和
是\
8 恰好为零
1 Scenario: The sum of all cash amounts exchanged must be zero
for derivatives
2
3  Given any derivative financial instrument
4  And a random date during its lifetime
5  When we generate the related cash flows on this date for the
payer and\
6  the receiver
7  Then the sum of the cash flows of the payer and the receiver
is\
8  exactly zero

此类场景通常使用诸如“给定任何购物车......”之类的句子。这种措辞对于常规场景来说是一种代码味道,但对于补充常规具体场景的基于属性的测试工具之上的面向属性的场景来说是可以的。

Such scenarios typically use sentences like “given ANY shopping cart….” This wording is a code smell for regular scenarios, but it’s okay for property-oriented scenarios on top of property-based testing tools supplementing the regular concrete scenarios.

创建术语表

理想的术语表是一个活生生的术语表,直接从代码中提取。然而,在许多情况下,无法创建动态术语表,您必须手动创建一个。

An ideal glossary is a living one, extracted directly from your code. However, in many cases it is not possible to create a living glossary, and you must manually create one.

可以手动创建词汇表作为 Markdown 文件并将其与其他功能文件放在一起。这样,它也将包含在实时文档网站中。您甚至可以将其作为虚拟的空特征文件。

It’s possible to create a glossary manually as a Markdown file and to co-locate it with the other feature files. This way, it will be included in the living documentation website, too. You could even do it as dummy empty feature file.

链接到非功能性知识

并非所有的知识都应该在同一个地方描述。您不想将领域知识与特定于 UI 或特定于遗留的知识混合在一起,这很重要,应该存储在其他地方。当该语言与领域语言相关时,您应该使用链接来表示关系并使其易于查找。

Not all the knowledge should be described in the same place. You don’t want to mix domain knowledge with UI-specific or legacy-specific knowledge, which is important and should be stored elsewhere. And when that language is related to the domain language, you should use links to represent the relationship and make it easy to find it.

正如本书其他部分所述,您可以使用不同的链接方法。您可以直接链接到 URL,如下所示,但无论何时更改,您都可能面临链接损坏的风险:

As described elsewhere in this book, you can use different approaches to linking. You may link directly to a URL, as shown here, although you risk of having a broken link whenever it changes:

1 https://en.wikipedia.org/wiki/Present_value
1  https://en.wikipedia.org/wiki/Present_value

您可以通过您维护的链接注册表来管理链接并将损坏的链接替换为有效的链接,如下所示:

You may go through a link registry that you maintain to manage links and to replace broken links with working ones, as shown here:

1 go/search?q=当前+值
1  go/search?q=present+value

您还可以使用加书签的搜索链接到包含相关内容的位置,如下所示:

You may also use bookmarked searches to link to places that include the related content, as shown here:

1 https://en.wikipedia.org/w/index.php?search=present+value
1  https://en.wikipedia.org/w/index.php?search=present+value

链接到非功能性知识为您提供了一种灵活的方式来链接到相关内容,但代价是让读者每次都选择最相关的结果。

Linking to nonfunctional knowledge gives you a resilient way to link to related content, at the expense of letting the reader select the most relevant results each time.

概括

Summary

BDD 是活文档的典型示例。它主要依赖于团队成员之间的频繁对话。它是构建软件所需工作的直接组成部分,但它以业务人员和开发人员都可以访问的形式保留了项目期间收集的知识。即使它会导致代码和场景中的冗余知识,随附的工具也可确保所有内容保持同步。但BDD只处理软件的业务行为。在后续章节中,我们将探讨如何将这些想法推广到与软件开发相关的其他活动。

BDD is the canonical example of living documentation. It primarily relies on frequent conversations between team members before anything else. It is a direct part of the necessary work to build software, and yet it preserves the knowledge collected during a project in a form that is accessible to both business people and developers. And even though it leads to redundant knowledge in the code and the scenarios, the accompanying tools make sure it all remains in sync. But BDD only deals with the business behaviors of the software. In subsequent chapters, we explore how to extrapolate these ideas for other activities related to software development.

第3章

Chapter 3

知识利用

Knowledge Exploitation

对于给定的项目或系统,很多知识已经存在,而且无处不在:在软件的源代码中,在各种配置文件中,在测试的源代码中,在应用程序运行时的行为中,在各种随机文件以及周围各种工具中以及所有相关人员大脑中的数据。

For a given project or system, a lot of knowledge already exists, and it’s everywhere: in the source code of the software, in the various configuration files, in the source code of the tests, in the behavior of the application at runtime, in various random files and as data within the various tools around, and in the brains of all the people involved.

传统文档试图以纸质或在线形式将知识收集到方便的文档中。这些文件重复了其他地方已经存在的知识。当其他文档是权威且可以信任时,这显然是一个问题,但它一直在演变。

Traditional documentation attempts to gather knowledge into convenient documents, in paper form or online. These documents duplicate knowledge that was already present elsewhere. This is obviously a problem when the other document is the authority and can be trusted, but it evolves all the time.

因为知识已经存在于许多地方,所以您需要做的就是建立机制,从知识所在的地方提取知识,并在需要时将其带到需要的地方。由于您没有太多时间来做这件事,因此此类机制必须轻量、可靠且省力。

Because knowledge already exists in many places, all you need to do is to set up mechanisms to extract the knowledge from where it’s located and bring it where it’s needed, when it’s needed. And because you don’t have much time for that, such mechanisms must be lightweight, reliable, and low effort.

识别权威知识

Identifying Authoritative Knowledge

学会发现系统中权威的知识来源非常重要。当知识在不同的地方重复时,你需要知道在哪里可以找到你可以信任的知识。当决策发生变化时,知识在哪里最准确地反映变化?

It’s important to learn to spot the authoritative sources of knowledge in your system. When knowledge is repeated in different places, you need to know where to find the knowledge that you can trust. When decisions change, where does the knowledge reflect the changes most accurately?

因此:找出所有权威知识所在的地方。对于给定的需求,建立自动化等机制来提取知识并将其转化为适当的形式。确保该机制保持简单并且不会分散注意力。

Therefore: Identify all the places where authoritative knowledge is located. For a given need, set up mechanisms such as automation to extract the knowledge and transform it into an adequate form. Make sure this mechanism remains simple and does not become a distraction.

有关软件如何工作的知识位于源代码中。在理想情况下,它很容易阅读,并且不需要任何其他文档。在不太理想的情况下,也许因为源代码自然就被混淆了,您只需要让这些知识更容易访问即可。

Knowledge about how the software works is in the source code. In the ideal case, it’s easy to read, and there is no need for any other documentation. In a not-so-ideal case, perhaps because the source code is naturally obfuscated, you just need to make this knowledge more accessible.

知识现在在哪里?

Where Is the Knowledge Now?

想象一下,一位同事或经理对您说:“给我有关 X 的文档!” 要处理此请求,您首先需要问自己或团队:“这些知识现在在哪里?”

Imagine that a colleague or manager says to you, “Give me documentation on stuff X!” To handle this request, you would first need to ask yourself or the team, “Where is this knowledge now?”

答案通常是显而易见的:知识存在于代码、功能测试或项目目标文档中。有时不太明显:知识就在人们的大脑中,无论他们是否知道。它甚至可能是在人与人之间,在这种情况下,你需要集体研讨会来阐明它。有些知识仅在评估工作软件期间存在,在运行时存在于程序的内存中。

The answer is often obvious: The knowledge is in the code, in the functional tests, or in the document on project goals. Sometimes it’s less obvious: The knowledge is in people’s brains, whether they know it or not. It may even be between people, in which case you’ll need collective workshops to elucidate it. Some knowledge exists only during the evaluation of the working software, in the memory of the program at runtime.

一旦找到权威知识的位置,如何利用这些知识并将其变成活的文档?

Once you’ve found the location of the authoritative knowledge, how can you harness that knowledge and make it living documentation?

当知识存在,但其形式对于目标受众和预期目的来说是不可访问或不方便的,则必须将其从单一的事实来源中提取为更容易访问的形式。此过程应该自动化,以发布版本明确的文档,并提供最新版本的链接。

When the knowledge is there but in a form that is not accessible or not convenient for the target audience and for the desired purpose, it must be extracted from its single source of truth into a more accessible form. This process should be automated to publish a clearly versioned document, with a link to the latest version.

有时知识无法提取。例如,也许业务行为不能简单地从代码中提取为英语业务句子,因此在这种情况下,您可以手动编写这些句子作为功能场景或测试。通过这样做,您会在知识中引入冗余,因此您需要一种协调机制来轻松检测不一致之处,如上一章所述。

Sometimes the knowledge can’t be extracted. For example, perhaps business behavior can’t simply be extracted as English business sentences from the code, so in this case, you write these sentences by hand as functional scenarios or tests. By doing so, you introduce redundancy to the knowledge, so you need a reconciliation mechanism to easily detect inconsistencies, as described in the previous chapter.

当知识分布在许多地方时,您需要一种方法将所有知识整合为一个聚合形式。当知识过剩时,仔细的选择过程(即管理过程)就至关重要。

When the knowledge is spread over many places, you need a way to do a consolidation of all the knowledge into one aggregated form. And when there is an excess of knowledge, a careful selection process—a curation process—is essential.

单一来源出版

Single-Source Publishing

将知识保存在单一事实来源中并在需要时从那里发布非常重要。当权威的知识来源是编程语言的源代码或正式语法的工具的配置文件时,通常有必要让无法阅读的受众也可以访问这些知识。标准方法是以每个人都能理解的格式提供文档,例如 PDF 文档中的简单英语,或者 Microsoft Office 文档、电子表格或幻灯片。但是,如果您直接创建这样的文档并包含所有相关的以复制粘贴的方式获取知识,当它发生变化时,你会遇到困难。在一个积极健康的项目上,你应该预料到它会发生很大的变化。

It’s important to keep the knowledge in one single source of truth and publish from there when needed. When the authoritative source of knowledge is source code in a programming language or a configuration file of a tool in a formal syntax, it’s often necessary to make this knowledge accessible to audiences that can’t read it. The standard way to do this is to provide a document in a format everyone understands, like plain English in a PDF document, or as a Microsoft Office document, spreadsheet, or slide deck. However, if you directly create such a document and include all the relevant knowledge in a copy-and-paste fashion, you will have a difficult time when it changes. And on an active and healthy project, you should expect that it will change a lot.

戴夫·亨特 (Dave Hunt) 和大卫·托马斯 (David Thomas) 所著的《实用程序员》表示,英语可以被视为一种编程语言。他们建议:“像编写代码一样编写文档:遵循 DRY 原则,使用元数据、MVC、自动生成等。” 作为重复的一个例子,Hunt 和 Thomas 提到规范文档中的数据库模式与 SQL 等正式语言中的数据库模式文件是冗余的。一种必须由另一种产生。例如,可以通过可以将 SQL 或 DDL 文件转换为纯文本和图表形式的工具来生成规范文档。

The Pragmatic Programmer by Dave Hunt and David Thomas says that English can be considered a programming language. They suggest: “Write documents as you would write code: honor the DRY principle, use metadata, MVC, automatic generation, and so on.” As an example of duplication, Hunt and Thomas mention that a database schema in a specification document is redundant with the database schema file in a formal language like SQL. One has to be produced out of the other. For example, a specification document could be produced by a tool that can convert the SQL or DDL file into plain text and diagram form.

因此:将每条知识都保存在一个具有权威性的地方。当必须将其提供给无法直接访问的受众时,请发布来自该单一知识源的文档。不要通过复制和粘贴将知识元素包含到要发布的文档中,而是使用自动化机制直接从单一权威知识源创建已发布的文档。

Therefore: Keep each piece of knowledge in exactly one place, where it’s authoritative. When it must be made available to audiences that can’t access it directly, publish a document out of that single source of knowledge. Don’t include the elements of knowledge into the document to be published by copying and pasting but use automated mechanisms to create a published document straight from the single authoritative source of knowledge.

图 3.1说明了如何通过自动化机制提取已有的权威知识以发布文档。

Figure 3.1 illustrates how the authoritative knowledge that is already present can be extracted by automated mechanisms in order to publish documents.

附图说明可以通过自动化机制将权威知识(显示知识源)转移到文档(显示已发布的文档)。

图3.1 从权威知识到发表文献

Figure 3.1 From authoritative knowledge to published documents

生成已发布文档的一些示例

Some Examples of Producing a Published Document

有许多工具可用于根据源代码和其他技术工件生成文档。这里有些例子:

There are many tools available to produce documents out of source code and other technical artifacts. Here are some examples:

  • GitHub: GitHub 采用 README.md 文件(该文件是有关整个项目目标的单一知识来源),并将其转换为呈现为漂亮的网页。

  • GitHub: GitHub takes the README.md file, which is a single source of knowledge about the goals of an overall project, and turns it into a web page that is rendered to be pretty.

  • Javadoc: Javadoc 提取代码的结构以及所有公共或私有 API,并将其作为参考文档发布到网站。您可以轻松地基于标准 Javadoc Doclet 创建自定义工具,以生成您自己的特定报告、术语表或图表,如第 6 章“自动化文档”中所述。

  • Javadoc: Javadoc extracts the structure and all the public or private API of the code and publishes it to a website as reference documentation. You can easily create a custom tool based on the standard Javadoc Doclet in order to generate your own specific report, glossary, or diagram, as described in Chapter 6, “Automating Documentation.”

  • Maven: Maven 和其他一些工具有一种内置的方式来生成一致的文档,通常作为网站,通过将许多工具报告和呈现的工件放在一起。例如,Maven 收集测试报告、静态分析工具报告、Javadoc 输出文件夹和任何 Markdown 文档,并将其全部组织到一个标准网站中。每个 Markdown 文档都可以在此过程中呈现。

  • Maven: Maven and some other tools have a built-in way of producing consistent documentation, usually as a website, by putting together a number of tool reports and rendered artifacts. For example, Maven collects test reports, static analysis tools reports, Javadoc output folders, and any Markdown documents and organizes it all into a standard website. Every Markdown document can be rendered in the process.

  • 精益酒吧:Leanpub,我用来写这本书的发布平台,是具有发布机制的单一来源的典型示例:每一章都写成一个单独的 Markdown 文件,图像保存在外部,代码可以放在自己的源文件中,并且甚至目录也在它自己的文件中。换句话说,内容以最方便使用的方式存储。每当我要求预览时,Leanpub 的出版工具链都会根据目录整理所有文件,并通过各种工具进行 Markdown 渲染、排版和代码突出显示,以生成多种格式的优质书籍:PDF、MOBI和 ePUB。这类似于出版界的小说手稿可以作为一本书、作为漫画出版,图 3.2)。您可以使用任何模板机制和一些自定义代码来遵循此基本模式。例如,您可以从资源文件中生成一个 PDF,其中列出了程序支持的每种货币。

  • Leanpub: Leanpub, the publishing platform I used to write this book, is a canonical example of single sourcing with a publication mechanism: Every chapter is written as a separate Markdown file, images are kept outside, the code can be in its own source files, and even the table of contents is in its own file. In other words, the content is stored in the way it’s most convenient to work with. Whenever I ask for a preview, Leanpub’s publishing toolchain collates all files according to the table of contents and renders it through various tools for Markdown rendering, typesetting, and code highlighting in order to produce a good-quality book in multiple formats: PDF, MOBI, and ePUB. This is similar to how the manuscript of a novel in the publishing world can be published as a book, as a comics, and then later as a movie—all from the same initial manuscript (see Figure 3.2). You could follow this basic pattern with any templating mechanism and a bit of custom code. For example, you could produce a PDF out of the resource file that lists every currency supported by the program.

一张说明“一个来源和可能多个文档”的图显示了一份手稿,首先指向书籍,然后指向漫画,然后指向电影。

图 3.2 一个来源和可能的多个文档

Figure 3.2 One single source and possibly multiple documents

带有版本号的已发布快照

A Published Snapshot with a Version Number

从单一事实来源发布的任何文档都是快照:因此必须将其视为严格不可变的,并且永远不应进行编辑。为了避免有人编辑已发布文档的风险,您应该优先选择防止编辑的文档格式,或者至少使编辑变得困难的文档格式。例如,与 Microsoft Office 文档相比,人们更喜欢 PDF,因为 PDF 很容易更改。无论格式如何,请考虑使用锁定标志来防止编辑。这并不是要让黑客无法进行编辑;而是要让黑客无法进行编辑。相反,我们的想法是让编辑变得足够困难,以便最容易更改权威来源并再次发布。

Any document published from a single source of truth is a snapshot: It must therefore be considered as strictly immutable and should never be edited. To avoid the risk of having someone edit a published document, you should favor document formats that prevent editing—or at least that make editing difficult. For example, prefer PDF over Microsoft Office documents, which are very easy to change. Whatever the format, consider using the locking flags to prevent edits. It’s not about making it impossible for hackers to edit; rather, the idea is to make it just hard enough to make edits that it is easiest to change the authoritative source and have it published again.

任何发布的文档都必须清楚地标识其版本,并且还应包含指向可以找到最新版本的位置的链接。

Any published document must clearly identify its version and should also include a link to the location where the latest version can be found.

如果您生成大量要打印的纸质文档,您可以考虑在每个文档上贴上条形码,其中包含指向始终包含最新版本的文件夹的链接。这样,即使是印刷文档也可以轻松地将读者引导至最新版本。

If you produce a lot of paper documents to be printed, you may consider putting on each of them a barcode with the link to the folder that always contains the latest version. This way, even a printed document can easily direct readers to the latest version.

评论

Remarks

您应该仅手动编写无法从现有项目工件中提取的内容,并且应该将此类注释存储在具有自己的生命周期的文件中。理想情况下,该文件的更改频率比从其他地方提取的知识要少得多。另一方面,如果您需要发布的文档中缺少某些信息,您应该尽一切努力将其添加到与其最相关的工件中,也许使用注释、标签或命名约定,或者将其作为新的协作工件本身。

You should write by hand only what cannot be extracted from an already existing project artifact, and you should store such remarks in a file that has its own lifecycle. Ideally, this file will change much less frequently than the knowledge extracted from other places. On the other hand, if some information is missing from a document you need to publish, you should by all means try to add it to the artifact it is most related to, perhaps using annotations, tags, or naming convention, or make it a new collaborative artifact on its own.

建立对账机制(又名验证机制)

Setting Up a Reconciliation Mechanism (aka Verification Mechanism)

每当知识在多个地方重复时,您应该建立一种协调机制来立即检测不一致之处。关于软件的知识重复是一件坏事,因为它需要重复工作来更新所有彼此冗余的地方,并且这也意味着当忘记更新时存在进入不一致状态的风险。

Whenever knowledge is repeated in more than one place, you should set up a reconciliation mechanism to detect inconsistencies immediately. Duplication of knowledge about software is a bad thing because it necessitates recurring work to update all the places that are redundant to each other, and it also means there is a risk of getting into an inconsistent state when an update is forgotten.

但是,如果必须有冗余,则可以通过使用验证机制来减轻痛苦,例如检查两个副本始终存在的自动测试同步。这并不能消除在多个地方进行更改的成本,但至少可以确保您不会忘记在某个地方进行更改。

However, if you must have redundancy, you can relieve the pain by using a verification mechanism, such as an automated test that checks that two copies are always in sync. This does not remove the cost of making changes in more than one place, but at least it ensures that you won’t forget a change somewhere.

大家都熟悉的一种对账机制是在餐厅检查账单(见图3.3)。你知道你吃了什么(并且证据可能仍然在菜肴的数量中可见),并且你检查账单上的每一行以确保没有差异。

One reconciliation mechanism everybody is familiar with is checking the bill in a restaurant (see Figure 3.3). You know what you ate (and the evidence may be still visible in the number of dishes), and you check each line on the bill to ensure that there’s no discrepancy.

图中显示了三个醒酒器、三个酒杯和四个盘子。 旁边的账单上分别写着饮料 4(标记为错误)、汤 2、菜肴 4,总计 225 美元。

图3.3 检查餐厅账单是一种对账机制

Figure 3.3 Checking the restaurant bill is a reconciliation mechanism

因此:当您想要或必须容纳存储在不同位置的知识中的冗余时,请确保使用协调机制使所有冗余知识保持一致。使用自动化确保一切保持同步,并立即检测到任何差异,并且您会收到警报提示修复它。

Therefore: When you want or have to accommodate a redundancy in the knowledge stored at various places, make sure all the redundant knowledge is kept consistent using a reconciliation mechanism. Use automation to make sure everything remains in sync and that any discrepancy is detected immediately, and you get an alert prompting to fix it.

运行一致性测试

Running Consistency Tests

正如第 2 章“行为驱动开发作为生活规范的示例”中提到的,使用 BDD,场景提供了行为的文档。每当场景和代码不一致时,您都会立即知道,因为测试自动化失败,就像 Roberval 天平一样(参见图 3.4)。

As mentioned in Chapter 2, “Behavior-Driven Development as an Example of Living Specifications,” with BDD, scenarios provide documentation of the behavior. Whenever a scenario and code disagree, you know it immediately because the test automation fails, much like a Roberval balance (see Figure 3.4).

下图说明了验证冗余知识是否同步并给出清晰状态的自动化机制。

图 3.4 验证冗余知识同步的自动化机制

Figure 3.4 Automated mechanism to verify that redundant knowledge is in sync

这种机制的实现得益于以自然领域语言解析场景以驱动其实现代码的工具。该代码是通过专门为此目的编写的一小层粘合代码(通常称为“步骤定义”)驱动的。这些步骤是解析的场景和正在驱动的实际代码之间的适配器。

This mechanism is made possible thanks to tools that parse the scenario in natural domain language to drive their implementation code. The code is driven through a little layer of glue code that you write specifically for that purpose, usually called “step definitions.” These steps are adapters between the parsed scenario and the actual code being driven.

想象一下测试以下场景:

Imagine testing the following scenario:

  • 指定方 BARNABA 被标记为破产

  • Given party BARNABA is marked as bankrupt

  • 交易 42 是针对 BARNABA,

  • And trade 42 is against BARNABA,

  • 当运行风险警报计算时,

  • When the risk alerting calculation is run,

  • 然后出现警报:触发针对破产方 BARNABA 的交易。

  • Then an alert occurs: Trade against the bankrupt party BARNABA is triggered.

该工具解析这些文本行,并将“给定方 BARNABA 被标记为破产”这句话识别为具有步骤定义的句子:

The tool parses these lines of text and recognizes the sentence “Given party BARNABA is marked as bankrupt” as one it has a step definition for:

1 Give("^一方(.*)被标记为破产$")
2公共无效partyMarkedAsBankrupt(string party){
3bankruptParties.put(party);
4 }
1 Given("^party (.*) is marked as bankrupt$")
2 public void partyMarkedAsBankrupt(string party){
3  bankruptParties.put(party);
4 }

该工具对每条线执行相同的操作。通常以When开头的句子触发实际计算,以then开头的句子提示工具检查断言:

The tool does the same for each line. Typically sentences starting with When trigger actual computation, and sentences starting with Then cue the tool to check assertions:

1 然后(“^警报:(/*) 被触发$”)
2公共无效anAlertIsTriggered(字符串预期消息){
3断言Equals(预期消息,实际消息);
4 }
1 Then("^an alert: (/*) is triggered$")
2 public void anAlertIsTriggered(string expectedMessage){
3  assertEquals(expectedMessage, actualMessage);
4 }

/*为了使所有这些发挥作用,句子需要使用参数(句子中间的正则表达式 [ ])实际驱动代码,并且断言必须尽可能精确地根据句子的期望进行检查。

For all of this to work, the sentences need to actually drive the code with parameters (the regular expression [/*] in the middle of the sentence), and the assertions must check against the expectations from the sentences as precisely as possible.

作为一个反例,在不从句子中提取参数的情况下对步骤进行编码是没有意义的,或者在进行一些更改后您将面临不一致的风险:

As a counter example, it would not make sense to code the step without extracting a parameter from the sentence, or you would run the risk of inconsistency after a few changes:

1 然后(“^警告:针对破产方 BARNABA 的交易是
触发$")
3公共无效anAlertIsTriggered(){
4 assertEquals("与破产方进行交易
安然”,实际消息);
5 }
1 Then("^an alert: Trade against the bankrupt party BARNABA is
triggered$" )
3 public void anAlertIsTriggered(){
4  assertEquals("Trade against the bankrupt party
ENRON",actualMessage);
5 }

不幸的是,即使硬编码消息不适合场景中的消息,场景仍然会通过,并且没有人会注意到。

The scenario would unfortunately still pass even when the hardcoded message does not fit the one in the scenario, and nobody would notice.

测试假设的调节

Reconciliation on the Test Assumptions

通常,您使用Give(或纯 xUnit 代码中的等效Arrange阶段)来创建模拟对象或将数据注入测试数据库。

Usually you use the Given (or the equivalent Arrange phase in plain xUnit code) to create mock objects or to inject data into the test database.

在测试遗留系统时,您通常必须处理几种类型的问题:

When testing legacy systems, you often have to deal with a couple types of problems:

  • 模拟数据库太难了,因此您必须以端到端的方式进行测试。

  • It’s too hard to mock the database, so you must test in an end-to-end fashion.

  • 您无法仅为了测试而重新创建或填充数据库,因此您必须使用真正的共享数据库,如果其他人正在使用该数据库,该数据库可以随时更改。

  • You can’t re-create or populate a database just for your tests, so you must work on a real shared database that can change anytime if someone else is working on it.

尽管存在这些问题,仍然可以使用与 xUnit 中的When语句或Arrange阶段完全相同的假设声明,但使用一个实现来检查假设是否仍然成立,而不是将值注入到模拟对象中:

Despite these problems, it’s still possible to use exactly the same declaration of an assumption as a When sentence or an Arrange phase in xUnit but with an implementation that checks that the assumption still holds true instead of injecting the value into a mock object:

1 Give("^一方(.*)被标记为破产$")
2公共无效partyMarkedAsBankrupt(string party){
3assertTrue(bankruptParties.isBankruptParty(party)); // 调用
数据库
4 }
1 Given("^party (.*) is marked as bankrupt$")
2 public void partyMarkedAsBankrupt(string party){
3  assertTrue(bankruptParties.isBankruptParty(party)); // calls
the DB
4 }

这不是测试的断言;这只是场景(或测试)有机会通过的先决条件。如果这个假设已经失败,那么该场景“甚至不会失败”。我经常将这种“测试前的测试”称为金丝雀测试此类测试可以告诉您即使在测试重点之外也存在问题,从而帮助您知道不必浪费时间在错误的地方进行调查。

This not an assertion of the test; it’s just a prerequisite for the scenario (or test) to even have a chance to pass. If this assumption already fails, then the scenario “does not even fail.” I often call this kind of “tests before the tests” a canary test. Such tests tell that something’s wrong even outside the test focus, helping you know that you don’t have to waste time investigating in the wrong place.

已发布的合同

Published Contracts

我第一次看到我的 Arolla 同事 Arnauld Loyer 使用的协调机制用于协调与第三方(例如调用您的服务的外部服务)的合同。如果您的服务公开带有参数 的资源CreditDefaultType(该参数有两个可能的值FAILURE_TO_PAY和 )RESTRUCTURING,则发布后您将无法按照您的意愿重命名它们。因此,您可以使用具有故意冗余的测试来强制合同的这些元素不会更改。您可以根据需要进行重构和重命名,但每当您违反合同时,协调测试都会向您发出测试失败的警报。

A reconciliation mechanism that I first saw used by my Arolla colleague Arnauld Loyer is used to reconcile contracts against third parties such as external services that call your services. If your services exposes a resource with the parameter CreditDefaultType, which has the two possible values FAILURE_TO_PAY and RESTRUCTURING, you can’t rename them as you wish once published. You can therefore use tests with a deliberate redundancy to enforce that these elements of the contract don’t change. You can refactor and rename as you wish, but whenever you break the contract, the reconciliation tests will alert you with a test failure.

这是强制文档的一个示例。理想情况下,您应该以可读的形式将测试作为合同的参考文档;API 领域的一些工具可以帮助您做到这一点。这里你肯定不想通过自动重构来更新测试;相反,您希望它超出重构范围,以便它保持不变以代表外部消费者服务。

This is an example of enforced documentation. Ideally you would make the test the reference documentation for the contract, in a readable form; some tools in the API sphere enable you to do this. Here you definitely don’t want to update the test through automated refactoring; rather, you want it out of reach of the refactoring so that it stays unchanged to represent the external consumer services.

这种方法最简单的实现如下所示,假设 的内部表示CreditDefaultType是一个名为 的 Java 枚举CREDIT_DEFAULT_TYPE

The most naive implementation for this approach would be something like that the following, assuming that the internal representation of the CreditDefaultType is a Java enum named CREDIT_DEFAULT_TYPE:

1 @测试
2 公共无效forceContract_CreditDefaultType
3  最终String[] 合约 = {"FAILURE_TO_PAY",
“重组”};
4
5   for (字符串类型:合约){
6assertEquals(类型,CREDIT_DEFAULT_TYPE.valueOf(类型)。
toString());
7}
8}
1 @Test
2 public void enforceContract_CreditDefaultType
3  final String[] contract = {"FAILURE_TO_PAY",
"RESTRUCTURING"};
4
5  for(String type : contract){
6  assertEquals(type, CREDIT_DEFAULT_TYPE.valueOf(type).
toString());
7  }
8 }

因为您希望确保遵守外部调用代码的约定,所以您再次将此约定定义为字符串数组,就好像它是从外部使用的一样。因为您想要检查合同是否正在使用传入和输出值进行磨练,所以您要确保合同字符串被识别为输入,并且valueOf()它是作为输出发送的字符串toString()

Because you want to make sure that the contract for the external calling code is respected, you define this contract again as an array of strings, as if it’s being used from the outside. And because you want to check that the contract is being honed with incoming and outcoming values, you make sure the contractual string is recognized as an input with valueOf() and that it’s the one being sent as an output with toString().

笔记

Note

这个例子只是为了解释这种协调机制的想法。在现实世界中,在测试中使用循环是一种不好的做法,因为如果出现异常,测试报告将无法准确说明问题所在的循环。相反,您可以使用参数化测试,将属于合约一部分的值集合作为参数源。

This example is only meant to explain the idea of this type of reconciliation mechanism. In the real world, it’s bad practice to use a loop inside a test as the test reporting will not tell precisely in which loop the problem resides if there is an exception. Instead, you would use a parameterized test, making the collection of values that are part of the contract the source of parameters.

通过这种方法,当最近加入团队的人决定重命名枚举常量时,测试立即无法表明不可能这样做,实际上就像防御性文档一样。这是对不当行为的防御,同时它为违规者提供了当场学习的机会:当测试失败时,他们了解到这个枚举常量是不应更改的合同的一部分。

With this approach, when someone who has recently joined the team decides to rename a constant of the enum, the test immediately fails to signal that it’s not possible to do that—in effect acting like defensive documentation. It’s a defense against misconduct, and at the same time it provides an opportunity for the violator to learn on the spot: When the test fails, they learn that this enum constant is part of a contract that should not be changed.

整合分散的事实

Consolidating Dispersed Facts

不同的事实放在一起就成为有用的知识。有时知识分布在许多地方。例如,具有接口和五个实现类的类型层次结构实际上可以在六个不同的文件中声明。包或模块的内容实际上可以存储在许多文件中。项目的完整依赖项列表实际上可能在其 Maven 清单 (POM) 及其父清单中部分定义。因此,需要收集和汇总许多小知识才能获得全面的了解。

Diverse facts put together become useful knowledge. Sometimes knowledge is spread over many places. For example, a type hierarchy with an interface and five implementing classes may actually be declared in six different files. The content of a package or module can actually be stored in many files. The full list of dependencies of the project may actually be defined partially in its Maven manifest (POM) and also in its parent manifest. Therefore, there is a need to collect and aggregate many little bits of knowledge to get a full picture.

例如,系统的整体图景是其各个部分的黑盒视图的并集,如图3.5所示。这里的整体知识是通过巩固机制得出的。

For example, the big picture of a system is the union of the black-box view of each of its part, as pictured in Figure 3.5. The overall knowledge here is derived through a consolidation mechanism.

一幅“从碎片化的权威知识到统一知识”的图展示了一个螺母、一个螺栓连接在一起形成螺母和螺栓。

图3.5 从碎片化的权威知识到统一的知识

Figure 3.5 From fragmented authoritative knowledge to unified knowledge

即使知识被分成许多小部分,仍然希望将所有这些小部分视为权威的单一事实来源。因此,派生的综合知识是从许多地方提取的已发表文档的特例。

Even if the knowledge is split into many little parts, it’s still desirable to consider all those little bits as the authoritative single sources of truth. The derived consolidated knowledge is therefore a special case of published document extracted from many places.

因此:设计一个简单的机制来自动合并所有分散的事实。该机制必须根据需要经常运行,以确保整体信息相对于部分而言是最新的。避免存储合并信息,除非存在缓存等技术问题。

Therefore: Design a simple mechanism to automatically consolidate all the dispersed facts. This mechanism must be run as often as necessary to ensure that the information about the whole is up-to-date with respect to the parts. Avoid any storage of the consolidated information, unless there are technical concerns like caching.

整合如何运作

How Consolidation Works

基本上,整合就像 SQL GROUP BY:您采用许多具有某些共同属性的事物,然后找到一种方法将所有这些多个事物转变为单个事物。实际上,它是通过扫描给定周长内的每个元素并增加结果来完成的,如图3.6所示。

Basically, consolidation is like a SQL GROUP BY: You take many things that have some properties in common, and you find a way to turn all those multiple things into a single thing. In practice, it’s done by scanning every element within a given perimeter, while growing the result, as shown in Figure 3.6.

一张说明“从分散的事实到有用的知识”的图显示了使用自动化机制将两个碎片化的权威知识转移到统一知识的已发布文档中。

图3.6 从分散的事实到有用的知识

Figure 3.6 From dispersed facts to useful knowledge

例如,要在一个项目的范围内从各个元素重新构建类层次结构的完整图片,有必要扫描项目的每个类和每个接口。扫描过程保留了迄今为止正在构建的每个层次结构的不断增长的字典(例如,映射层次结构顶部 > 子类列表)。每次扫描遇到扩展另一个类或扩展接口的类时,都会将其添加到字典中。

For example, to reconstitute the full picture of a class hierarchy within the limits of one project from its individual elements, it’s necessary to scan every class and every interface of the project. The scanning process keeps a growing dictionary of every hierarchy under construction so far in the process (for example, with the mapping top of hierarchy > list of subclasses). Every time the scan encounters a class that extends another class or that extends an interface, it adds it to the dictionary.

扫描完成后,字典包含项目中所有类型层次结构的列表。当然,可以将流程简化为仅满足特定文档需求的感兴趣层次结构的子集,例如将扫描限制为属于已发布 API 的类和接口。

When the scan is done, the dictionary contains a list of all type hierarchies in the project. Of course, it’s possible to reduce the process to only a subset of the hierarchies of interest for a particular documentation need, such as restricting the scan to classes and interfaces that belong to a published API.

再举一个例子,您可以创建一个由较小组件组成的系统的黑盒生活图,每个组件都有自己的一组输入和输出,如图 3.7所示

As another example, you can create a black-box living diagram of a system made of smaller components, each of which has its own set of inputs and outputs, as shown in Figure 3.7.

显示整个系统(及其输入和输出)的黑盒视图的图可以通过合并其具有自己独立的输入和输出的组件的黑盒视图来导出。

图3.7 通过整合各组件的黑盒视图可以得出整个系统的黑盒视图

Figure 3.7 The black-box view of the whole system can be derived through a consolidation of the black-box view of its components

简单的合并可以收集每个组件的每个输入和输出的并集。更复杂的合并可以尝试删除内部相互匹配的每个输入和输出。您可以决定如何针对特定需求进行整合。

A simple consolidation can just collect the union of every input and output from each component. A more sophisticated consolidation can try to remove every input and output that match each other internally. It’s up to you to decide how you want consolidation to happen for a particular need.

合并实施注意事项

Consolidation Implementation Considerations

像往常一样,如果可能的话,您应该重用已有的可以进行所需整合的工具。例如,一些 Java 代码解析器可以提供类型层次结构。如果工具中没有您想要的内容,您可以添加它,例如通过在编程语言抽象语法树 (AST) 2上编写另一个访问者1。一些更强大的工具甚至提供自己的语言来非常有效地查询代码库。如果您必须执行非常复杂的查询,您可能希望将 AST 加载到图形数据库中,但如果您这样做,恐怕您将成为文档工具的软件供应商。

As usual, if possible, you should reuse a tool you already have that can do a desired consolidation. Some parsers for Java code can provide type hierarchies, for example. If what you want is not already in the tool, you can add it, such as by writing another visitor1 on the programming language Abstract Syntax Tree (AST)2. Some more powerful tools even provide their own language to query the code base very efficiently. You might want to load the AST into a graph database if you have to do very complex queries, but if you do that, I’m afraid you’re becoming a software vendor of documentation tools.

1. Erich Gamma、John Vlissides、Ralph Johnson 和 Richard Helm 所著的《设计模式:可重用面向对象软件的元素》一书中的“访问者模式”艾迪生-韦斯利。

1.“The Visitor Pattern” from the book Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma, John Vlissides, Ralph Johnson, and Richard Helm. Addison-Wesley.

2.解析器用来表示和操作源代码结构的通用树结构。

2.A common tree structure used by parsers to represent and manipulate the structure of the source code.

如果出于性能问题而将派生知识保存在缓存中,请确保它不会成为事实来源,并且始终可以正确删除它,然后从所有事实来源从头开始重建。

If the derived knowledge is kept in a cache for performance issues, make sure it does not become a source of truth and that it can always be properly dropped and then rebuilt from scratch from all the sources of truth.

对于大多数系统,可以以批处理方式按顺序扫描所有部件。这通常是在构建过程中完成的,它会生成一个合并,可以在项目网站上发布或作为报告发布。

For most systems, it is possible to scan all parts in sequence in a batch-processing fashion. This is typically done during a build, and it produces a consolidation that is ready for publication on the project website or as a report.

对于诸如信息系统之类的大型系统,运行按顺序扫描所有部分的计算是不切实际的。在这种情况下,整合过程可以逐步完成。例如,每个部分的构建可能会通过将数据推送到共享位置(例如共享数据库)中某处的整体合并状态来贡献部分更新。这种合并状态是派生信息;它的可信度低于每个版本的信息。如果出现任何问题,您应该放弃它并让它从每个构建的贡献中再次增长。

For a large system such as an information system, it is not practical to run calculations that scan all parts in sequence. In such a case, the consolidation process may be done incrementally. For example, the build of each part might contribute a partial update by pushing data to an overall consolidation state somewhere in a shared place, such as a shared database. This consolidation state is derived information; it is less trusted than the information from each build. If anything goes wrong, you should drop it and let it grow again from the contributions of each build.

现成的文档

Ready-Made Documentation

您所做的大部分工作已经记录在文献中。并非所有知识都特定于您的背景;许多知识是通用的,并与业内许多其他公司的许多其他人共享。思考有关编程语言、开发工具以及软件模式和实践的所有知识;其中大部分是行业标准。

Most of what you do is already documented in the literature. Not all knowledge is specific to your context; a lot of knowledge is generic and shared with many other people in many other companies in the industry. Think about all the knowledge on programming languages, developer tools, and software patterns and practices; most of that is industry standard.

我们每天所做的越来越多的事情被才华横溢的实践者整理成模式、技术和实践。所有这些知识都被正确记录在世界各地的书籍、博客文章、会议演讲和研讨会中。这是现成的文档,可以免费或以书籍或研讨会的价格获得。这里有一些例子:

More and more of what we do every day gets codified by talented practitioners into patterns, techniques, and practices. And all that knowledge is properly documented in books, blog posts, and conference talks and workshops around the world. This is ready-made documentation that’s readily available, for free or for the price of a book or workshop. Here are a few examples:

  • 测试驱动开发,肯特·贝克

  • Test-Driven Development, Kent Beck

  • 设计模式:可重用面向对象软件的元素,Erich Gamma、John Vlissides、Ralph Johnson 和 Richard Helm

  • Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma, John Vlissides, Ralph Johnson, and Richard Helm

  • 企业应用程序架构模式,Martin Fowler

  • Patterns of Enterprise Application Architecture, Martin Fowler

  • 领域驱动设计,埃里克·埃文斯

  • Domain-Driven Design, Eric Evans

  • C2 wiki 上的所有内容

  • Everything on the C2 wiki

  • 杰里·温伯格的每一本书

  • Every book from Jerry Weinberg

  • 持续交付,Jez Humber 和 Dave Farley

  • Continuous Delivery, Jez Humber and Dave Farley

  • 所有干净代码文献

  • All the clean code literature

  • Git 工作流程策略

  • Git workflow strategies

可以肯定地说,如果您能思考一下,就已经有人写过相关文章了。模式、标准名称和标准实践都存在,即使您还不认识他们。文献在不断增长,而且已经如此之大,以至于您无法了解全部,否则您将花费​​大量时间阅读,而您将没有任何时间来创建软件。

It is probably safe to say that if you can think about it, somebody has already written about it. Patterns, standard names, and standard practices exist, even if you don’t know them yet. The literature is growing, and it’s already so huge that you cannot know it all, or you would spend so much time reading you would not have any time left to create software.

笔记

Note

在《学徒模式》一书中,戴夫·胡佛和阿德瓦勒·奥希内耶提倡学习经典。例如,他们建议首先阅读阅读列表中最旧的书籍。

In the book Apprenticeship Patterns, Dave Hoover and Adewale Oshineye advocate studying the classics. For example, they suggest reading the oldest books in your reading list first.

关于成熟商业行业的知识也是通用知识。即使在竞争非常激烈的领域,例如金融定价或电子商务中的供应链优化,大多数知识都是公开的并且可在行业标准书籍中找到,只有一小部分业务知识是特定的和机密的,并且仅适用于特定的领域。就在那时。

Knowledge about mature business industry sectors is also generic knowledge. Even in very competitive areas like pricing in finance or supply chain optimization in e-commerce, most of the knowledge is public and available in industry-standard books, and only a small part of the business knowledge is specific and confidential— and only for a while at that.

例如,每个业务领域都有自己的基本阅读清单,并且可能有一本通常被称为该领域“圣经”的书(例如约翰·C·赫尔(John C Hull)的《期权、期货和其他衍生品》、《物流和金融)供应链管理,作者:马丁·克里斯托弗 (Martin Christopher)。

For example, each business domain has its own essential reading list, and it might have a book that is often referred to as “The Bible” of that field (for example, Options, Futures, and Other Derivatives by John C Hull, Logistics and Supply Chain Management by Martin Christopher).

好消息是通用知识已经记录在行业文献中。有书籍、博客文章和会议演讲对此进行了很好的描述。有标准词汇可以谈论它。有一些培训可以让知识渊博的人更快地学习它。

The good news is that generic knowledge is already documented in the industry literature. There are books, blog posts, and conference talks that describe it quite well. There are standard vocabularies to talk about it. There are trainings available to learn it faster from knowledgeable people.

通用知识本身就是一个已解决的问题。这些知识是现成的,可供每个人重复使用。如果您已在系统中应用了这些通用知识,则只需链接到正确的文献即可完成记录。

Generic knowledge in itself is a solved problem. This knowledge is ready-made, ready to be reused by everyone. If you have applied this generic knowledge in your system, you just have to link to the right literature and you’re done documenting.

因此:考虑到大多数知识已经记录在行业文献中。做好功课并在网络上寻找标准的知识来源或询问其他知识渊博的人。不要尝试再次记录别人已经写得很好的东西;而是链接到它。并且不要尝试原创;相反,尽可能采用标准做法和标准词汇。

Therefore: Consider that most knowledge is already documented somewhere in the industry literature. Do your homework and look for the standard source of knowledge on the web or by asking other knowledgeable people. Don’t try to document again something that’s already been well written by someone else; link to it instead. And don’t try to be original; instead, adopt the standard practices and the standard vocabulary as much as possible.

在大多数情况下,有意采用行业标准是一种胜利。您正在做的事情几乎肯定已经在某个地方被覆盖了。如果你运气不好,它只会在一两个博客中出现。如果幸运的话,这是行业标准。无论哪种方式,您都需要找到它被覆盖的位置,原因如下:

In most cases, deliberately adopting industry standards is a win. What you’re doing is almost certainly already covered somewhere. If you’re unlucky, it will be only in a blog or two. If you’re lucky, it’s industry standard. Either way, you need to find where it is covered, for several reasons:

  • 您可以参考其他来源,而不是自己编写。

  • You can refer to other sources instead of doing the writing yourself.

  • 其他来源可能会建议您未考虑过的改进或替代方案。

  • Other sources might suggest improvements or alternatives that you haven’t considered.

  • 其他来源可能比您更深入地描述情况,为您提供外部见解。

  • Other sources might describe the situation in a deeper way than you did, giving you external insights.

  • 这样的描述可以验证您的方法是否有意义。如果您找不到任何帐户,请小心。

  • Such a description can validate that your approach makes sense. If you cannot find any account, beware.

  • 最重要的是,您将了解地球上其他地方如何谈论这种情况。

  • Most importantly, you will learn how the rest of the planet talks about this situation.

标准词汇的力量

The Power of a Standard Vocabulary

谁控制了词汇,谁就控制了思想。

He who controls vocabulary controls thought.

——路德维希·维特根斯坦

—Ludwig Wittgenstein

与世界上其他人使用相同的词语进行交谈是一个巨大的优势。它使您能够使用较短的句子进行交流。如果没有通用词汇,您可能会花费几句话来尝试描述文本编辑器的设计:

Talking using the same words as everybody else in the world is a fantastic advantage. It enables you to communicate using shorter sentences. Without a common vocabulary, you could spend several sentences trying to describe the design of a text editor:

内联编辑是通过具有多个子类的接口来完成的。文本编辑器将实际处理委托给界面,而不必关心哪个子类实际上在执行这项工作。根据内联编辑是打开还是关闭,使用不同子类的实例。

Inline editing is done thanks to an interface with several subclasses. The text editor delegates the actual processing to the interface, without having to care which subclass is actually doing the job. Depending on whether the inline editing is on or off, an instance of a different subclass is used.

但是,如果您熟悉设计模式等标准记录知识,则可以更简洁地表达您的观点:

However, if you’re familiar with standard documented knowledge like design patterns, you can get your point across more concisely:

“内联编辑是作为控制器中的状态实现的。” 3

“Inline editing is implemented as a State in the Controller.”3

3.这个关于状态模式的句子示例来自 Kent Beck, https://www.facebook.com/notes/kent-beck/entropy-as-understood-by-a-programmer-part-1-program-struct /695263730506494

3.This example of sentence on the State Pattern is from Kent Beck, https://www.facebook.com/notes/kent-beck/entropy-as-understood-by-a-programmer-part-1-program-structure/695263730506494

每个成熟的行业都有自己丰富的术语,因为使用这些易于理解的术语是一种有效的沟通方式。汽车中的每个零件都有一个特定的名称,具体取决于其在车辆中的作用:轴不仅仅是轴它是凸轮轴曲轴气缸中有一个活塞,还有推杆正时链条。领域驱动设计提倡在领域中谨慎地发展这种无处不在的语言。

Each mature industry has its own rich jargon because using such understood jargon is an efficient way to communicate. Every part in a car has a specific name, depending on its role in the vehicle: A shaft is not just a shaft, it’s a camshaft or a crankshaft. There’s a piston in a cylinder, and there are pushrods and a timing chain. Domain-driven design advocates for carefully growing such ubiquitous language in the domain.

每当标准词汇量增长时,软件行业就会取得进步。例如,每当 Martin Fowler 为我们不假思索的模式创造另一个术语时,他就在帮助我们为我们的行业发展自己的无处不在的语言。

The software industry makes progress each time its standard vocabulary grows. For example, whenever Martin Fowler coins another term for a patterns that we do without thinking about it, he is helping grow our own ubiquitous language for our industry.

通用语言对文档非常有帮助。如果您知道自己在做什么并且知道它在行业中的名称,那么您只需插入对行业标准的引用,就可以以较低的成本获得广泛的文档。

Ubiquitous language is extremely helpful in documentation. If you know what you’re doing and you know what it’s called in the industry, you can just insert a reference to the industry standard, and you have achieved extensive documentation at low cost.

模式和模式语言对于将现成的知识打包到可重用的文档中特别有效。模式实际上是罐装文档。他们创建了一个标准词汇表,您可以使用和参考以获得完整的参考。

Patterns and pattern languages are particularly effective at packing ready-made knowledge into a reusable documentation. Patterns really are canned documentation. They create a standard vocabulary you can use and refer to for complete reference.

设计模式是有经验的程序员的沟通工具。不是初学者的辅助轮或脚手架。

Design patterns are communication tools for experienced programmers. Not training wheels or scaffolding for beginners.

—推特上的@nycplayer

—@nycplayer on Twitter

100% 设计模式?

100% Design Patterns?

模式很重要。但当我开始学习设计模式时,我会尽可能地使用它们;这种情况很常见,有些人甚至称之为模式炎。然后我变得理性并学会了何时不使用模式。

Patterns matter. But when I started learning about design patterns, I was trying to use them whenever I could; this is so common that some even call it patternitis. Then I became reasonable and learned when not to use patterns.

许多文章严厉批评了充满模式的代码。然而,我认为他们忽略了一点:你应该学习尽可能多的模式。我们的想法并不是为了使用模式而学习模式,尽管它们可能很有用。相反,重点是了解许多模式,以便了解您正在做的事情的标准名称。按照这种观点,100% 的代码可以而且应该通过模式来描述。

Many articles have harshly criticized code that is full of patterns. However, I think they miss the point: You should learn as many patterns as you can. The idea is not to learn patterns in order to use them, though they can be useful; instead, the point is to know many patterns in order to know the standard names of what you’re doing. In this view, 100% of the code could, and should, be described by the means of patterns.

了解标准词汇还可以打开获得更多知识的大门:您可以找到有关您感兴趣的主题的书籍并购买培训。您还可以找到具有这些知识的人员并雇用他们。

Knowing the standard vocabulary also opens the door to even more knowledge: You can find books and buy training on the topic you’re interested in. You can also pinpoint people with this knowledge and hire them.

了解标准词汇并不意味着找到解决方案。即使您有一个完美的解决方案,也值得了解一下它在行业中的名称。标准词汇表使您能够参考其他人的工作,他们以精心编写、经过同行评审和经过时间考验的方式描述了解决方案。

Knowing the standard vocabulary is not so much about finding a solution. Even when you have a perfect solution, it’s worth finding out what it’s called in the industry. A standard vocabulary enables you to refer to the work of other people who describe a solution in a well-written, peer-reviewed, and time-tested fashion.

链接到标准知识

Linking to Standard Knowledge

通用知识已经记录在行业文献、书籍和在线中。使用时,请通过互联网链接或参考书目链接到权威来源。如果某件事曾经写得很好,你应该参考它,而不是重新做一次较差的记录工作。

Generic knowledge is already documented in the industry literature, in books, and online. When you use it, link to the authoritative source via an Internet link or a bibliographic reference. If something has been written well once, you should refer to it rather than redo a poorer job of documenting it again.

当然,一个大问题可能是确定某条知识的标准名称。Google 等搜索引擎以及 C2 和 Stack Overflow 等社区网站是您的朋友。您可能必须猜测其他人如何谈论某个话题。然后,您可以快速扫描第一个搜索引擎结果,以确定更准确的词汇表,可以帮助您进行更窄的查询。通过这种探索,您将很快学到很多东西,并了解该主题的多少内容已经被编码以及使用了哪些术语。

Of course, one big problem can be identifying the standard name for a piece of knowledge. Search engines like Google and community websites like C2 and Stack Overflow are your friends. You might have to guess how others talk about a topic. Then you can quickly scan the first search engine results to determine a more accurate vocabulary that can help you make narrower queries. Through this exploration, you’ll quickly learn a lot and get to see how much of the topic is already codified and using what terminology.

不要犹豫,在您的团队或论坛上四处询问建议。其他人可能有经验和资历,并且有更多的时间来索引他们多年来遇到的标准知识的指针,即使是表面的。

Don’t hesitate to ask around—in your team or on a forum—for suggestions. Other people may have experience and seniority and more time to index pointers to standard knowledge they’ve encountered, even superficially, over the years.

从给定的术语,您还可以浏览维基百科以及文章末尾的所有各种链接。还要留意底部的“相关”链接,直到您认识到自己的情况。维基百科是一个很棒的工具,可以将标准词汇映射到您自己的心理世界。

From a given term, you can also browse Wikipedia and all the various links at the ends of the articles. Keep an eye on the bottom “related” links as well, until you recognize your situation. Wikipedia is a fantastic tool for mapping a standard vocabulary to your own mental universe.

不仅仅是词汇

More Than Just Vocabulary

拥有共享的标准词汇是有效进行口头和书面沟通的关键优势。也就是说,即使是标准描述也可能涉及您未考虑过的改进和替代方案。这些信息也很有用。现成的文档实际上是可重用的思维,这是有很大帮助的。这有点像作者(通常是经验丰富的实践者)靠近您,以便您可以一起思考。

Having a shared standard vocabulary is a key advantage in communicating effectively, both verbally and in writing. That said, even a standard description may involve refinements and alternatives you haven’t considered. That information is useful, too. Ready-made documentation is in fact reusable thinking, which is a great help. It’s a bit like having the author, usually a seasoned practitioner, close to you so you can think together.

你仍然需要思考。但您不必独自完成。

YOU STILL HAVE TO THINK. BUT YOU DONT HAVE TO DO IT ALONE.

—推特上的@michelesliger

—@michelesliger on Twitter

如果我说“我在遗留子系统之上创建一个适配器”,这句话用几句话就意味着很多事情,因为适配器模式的想法不仅仅是一个名称。例如,此模式的一个重要结果是适配器(我们示例中的遗留子系统)不应该知道适配器;只有适配器应该知道遗留子系统。

If I say that “I create an adapter on top of the legacy subsystem,” this sentence implies a lot of things in a few words, because there’s more than a name in the idea of the adapter pattern. For example, an important consequence in this pattern is that the adaptee—the legacy subsystem in our example—should not know about the adapter; only the adapter should know about the legacy subsystem.

当我说一个包代表表示层,而另一个包代表领域层时,我还暗示只有前者可以依赖后者,而不能反过来。

When I say that a package represents the presentation layer, whereas this other package represents the domain layer, I also imply that only the former can depend on the latter—and never the other way around.

数学中的常态是重用定理和共享文献中的抽象结构,以进一步发展,而无需重新发明和一次又一次地证明相同的结果。标准词汇也是如此。

It’s the norm in mathematics to reuse theorems and shared abstract structures from the literature to go further without reinventing and having to prove the same results again and again. The same goes for a standard vocabulary.

在对话中使用现成的知识来加速知识转移

Using Ready-Made Knowledge in Conversation to Speed Up Knowledge Transfer

以下是我与我的朋友 Jean-Baptiste Dusseaut(简称“JB”,Twitter 上的@BodySplash)的一段简短对话,旨在说明共同的文化和词汇如何帮助有效地共享知识(见图 3.8

Here’s a little conversation I had with my friend Jean-Baptiste Dusseaut (“JB” for short, @BodySplash on Twitter) to illustrate how a common culture and vocabulary can help with sharing knowledge efficiently (see Figure 3.8).

图中展示了两个卡通人物 JB 和 CM,其中 CM 说:“你好,JB。我听说你推出了一家新公司,Jam shake,是关于什么的?”

图 3.8 你好,JB。我听说你创办了一家新公司 Jamshake,它是做什么的?

Figure 3.8 Hello, JB. I heard you launched a new startup, Jamshake—what is it about?

CM:你好,JB。我听说你创办了一家新公司 Jamshake。这是关于什么的?

CM: Hello, JB. I heard you launched a new startup, Jamshake. What is it about?

JB:它是音乐家的社交和协作工具。我们提供了一个轻量级的社交网络来寻找其他音乐家和很酷的项目,还提供了一个浏览器内数字音频工作站 Jamstudio,以便与其他音乐家实时协作。它是一种音乐 Google 文档(见图3.9)。

JB: It’s a social and collaborative tool for musicians. We provide both a lightweight social network to find other musicians and cool projects and an in-browser digital audio workstation, the Jamstudio, to collaborate in real time with other musicians. It’s a kind of Google Doc for music (see Figure 3.9).

图中卡通人物 JB 说道:“它?音乐家的社交和协作工具。我们提供一个轻量级社交网络来寻找其他音乐家和很酷的项目,还提供一个浏览器内数字音频工作站 Jam 工作室,以便在现实中进行协作与其他音乐家一起度过的时间。这有点像音乐的 Google 文档”。

图 3.9 这是音乐家的社交和协作工具

Figure 3.9 It’s a social and collaborative tool for musicians

CM:听起来真的很酷!在技​​术方面,您的系统简而言之是如何组织的?

CM: Sounds really cool! On the technical side, how’s your system organized in a nutshell?

JB:我知道您熟悉软件工艺和设计,尤其是 DDD,所以当您听说我们的系统由多个子系统组成,其中一个子系统由有界上下文组成时,您不会感到惊讶(见图 3.10

JB: I know you’re familiar with software craftsmanship and design, and DDD in particular, so you won’t be surprised to hear that our system is made of several subsystems, one by bounded context (see Figure 3.10).

图中显示卡通人物 JB 说道:“我知道您熟悉软件工艺和设计,尤其是 DDD,因此当您听到我们的系统由多个子系统组成(其中一个子系统由有界上下文组成)时,您不会感到惊讶”。

图3.10 我知道你熟悉软件工艺和设计

Figure 3.10 I know you’re familiar with software craftsmanship and design

CM:哦,是的,非常有道理!那么每一个都是一个微服务吗?

CM: Oh yes, makes perfect sense! Each one is a microservice then?

JB:是的,也不是。它们开始时是在依赖关系方面非常严格解耦的模块,也就是说,准备在运行时提取到自己的进程中。但是我们将它们保留在同一个进程中,直到我们真正需要单独的进程为止,通常是随着负载的增加而扩展。

JB: Yes and no. They begin as modules very strictly decoupled in terms of dependencies—that is, ready to be extracted into their own process at runtime. But we keep them within the same process until we really need the separate processes, usually to scale with the increasing load.

厘米:是的!我将其称为“微服务就绪”代码风格。您不必预先支付太多物理服务的全部费用,只要您可以随时选择这样做即可。但这需要纪律。

CM: Yes! I call that the “microservice-ready” style of code. You don’t have to pay the full cost of too many physical services up front, as long as you have the option to do it at any time. But it takes discipline.

JB:是的,当您只是一两个开发人员时,这很容易,就像我们现在一样。在实践中,由于负载不断增加,我们经常使用这些选项。

JB: Yes, and that’s easy when you’re just one or two developers, like we are at the moment. In practice, we regularly use these options because of the increasing load.

CB:增加负载:当您是一家正在成长并寻求融资的初创公司时,这是一个很好的问题!

CB: Increasing load: that’s a good problem to have when you’re a startup growing and looking for financing!

JB:是的,绝对如此。

JB: Yes, absolutely.

CB:我想了解一下整个系统的概况。也许是有界上下文的有界上下文?

CB: I’d like to have an overview of the full system. Perhaps Bounded Context by Bounded Context?

JB:当然。目前大约有五个有界上下文:获取(新用户的注册)、安排、音频渲染(混合、限制器和压缩类型的处理)、Stems 管理和报告。除了在 S3 存储之上使用 Node.js 构建的 Stems 管理之外,它们都依赖于单独的 Postgres 数据库之上的 Spring Boot 实例。每个限界上下文都关注其领域模型,除了注册,它是基于 Hibernate 的 CRUD-y。它是该系统早期版本的幸存者。

JB: Sure. There are around five bounded contexts at the moment: Acquisition (registration of new users), Arrangement, Audio Rendering (mixing, limiter, and compression kinds of processing), Stems Management, and Reporting. They all rely on Spring Boot instances on top of separate Postgres databases, except for the Stems management built with Node.js on top of an S3 storage. Each Bounded Context is paying attention to its domain model, except Registration, which is CRUD-y based on Hibernate. It’s a survivor from the early version of the system.

CB:我现在已经清楚地了解了它是什么样的(见图3.11)。非常感谢,JB!

CB: I now have a clear picture in mind of what it’s like (see Figure 3.11). Thanks a lot, JB!

图中显示了卡通人物在听 JB 讲话时描绘整个系统的情况。

图 3.11 在听 JB 讲话时描绘整个系统

Figure 3.11 Picturing the full system while listening to JB talking

更加刻意地工作是否会反对直觉和自发性?

Does Working More Deliberately Oppose Gut Feeling and Spontaneity?

什么时候应该认为有意识地了解某件事不如直觉呢?请考虑 Steve Hawley 的一篇有趣帖子中的信息:

When should knowing something consciously be considered less desirable than gut feeling? Consider this information from an interesting post by Steve Hawley:

图案的使用就像文学手段的使用一样。(可能)有无数种方式可以表达相同的一般思想,但我怀疑你会找到一个优秀的作家,他在开始章节时会思考:“我在这里介绍一个角色,所以最好画出来角色的照片。这需要比喻。是的,比喻就可以了。我想我还会使用一些讽刺性的并置。” 这种写法让人感觉很牵强。我读过一些代码,其中设计模式的应用也让人感到被迫。4

The use of patterns is like the use of literary device. There are (probably) an infinite number of ways in which the same general thought can be expressed, but I doubt you will find a single quality writer who started off a chapter thinking, “I’m introducing a character here so it’s best to paint a picture of the character. That calls for simile. Yeah, simile will do it. I think I’ll also use some ironic juxtaposition.” This type of writing feels forced. I’ve read code where the application of design patterns also felt forced.4

4. https://www.atalasoft.com/cs/blogs/stevehawley/archive/2009/07/29/design-patterns-and-practice.aspx

4.https://www.atalasoft.com/cs/blogs/stevehawley/archive/2009/07/29/design-patterns-and-practice.aspx

史蒂夫在这里说得有道理。我必须承认,如果对高质量的例子进行适当的训练,直觉可能比有意识地追求完美更有优势,也许是因为我们的大脑比我们经常意识到的更强大。是的,很多时候,我们假装我们所做的事情是有意而为之的,尽管我们只是在事后解释一个实际上是基于直觉的决定。

Steve has a point here. I must admit that gut feeling, if trained properly on examples of good quality, may have advantages over a conscious quest for perfection, perhaps because our brains are more powerful that we often realize. And yes, very often, we pretend that what we have done was intentional and deliberate, even though we’re just explaining, after the fact, a decision that was actually based on gut feeling.

Propel 的 Francois 提出了这个有趣的问题:开发人员应该了解设计模式吗?ORM 引擎是相当复杂的软件,它们大量(并且有意)使用模式,特别是 Fowler PoEAA 模式。Francois 在博客文章中讨论了在引擎文档中提及或不提及 Propel ORM 核心使用的各种模式的原因:

Francois from Propel has raised this interesting question: Should developers know design patterns? ORM engines are rather sophisticated pieces of software, and they make big (and deliberate) use of patterns, in particular the Fowler PoEAA patterns. Francois discusses in a blog post the reasons to mention or not mention the various patterns used at the heart of the Propel ORM in the documentation of the engine:

Propel 与其他 ORM 一样,实现了许多常见的设计模式。活动记录、工作单元、身份映射、延迟加载、外键映射、具体表继承、查询对象等等都出现在 Propel 中。对象关系映射的想法本身确实是一种设计模式。

Propel, like other ORMs, implements a lot of common Design Patterns. Active Record, Unit Of Work, Identity Map, Lazy Load, Foreign Key Mapping, Concrete Table Inheritance, Query Object, to name a few, all appear in Propel. The very idea of an Object-Relational Mapping is indeed a Design Pattern itself.

如果你了解模式,你就能很快理解 Propel;如果不这样做,您将需要进行更多解释才能达到更高水平的专业知识,并且下次遇到另一个 ORM 时,您将不得不重做此发现工作。当然,在某些时候你会认出这些模式,只是不知道它们的名字。你只是对这些模式有半意识。

If you know the patterns, you can understand Propel quickly; if you don’t, you’ll need to go through a lot more explanation to reach a higher level of expertise, and next time you encounter another ORM, you’ll have to redo this discovery effort. Of course, at some point you’ll recognize the patterns, and you just won’t know their names. You’d just be half-conscious of the patterns.

工具历史

Tools History

正如您之前所看到的,很多知识已经存在,其中一些知识隐藏在您使用的工具的历史记录中。源代码控制系统就是一个明显的例子。他们知道每一次提交——何时完成、由谁完成、进行了哪些更改——并记住每一次提交评论。有些工具,例如 Jira 甚至您的电子邮件客户端,也非常了解您的项目。

As you’ve seen before, a lot of knowledge is already there, and some of it is hidden in the history of the tools you use. Source control systems are an obvious example. They know about every commit—when it was done, by whom, what changes were made—and remember each commit comment. Some tools, like Jira or even your email client, also know a lot about your project.

然而,这些知识并不总是容易获得,而且也没有得到充分利用。例如,如果没有屏幕可以方便地检索聊天中最常见的问题,您可能永远不会知道。

However, this knowledge is not always readily accessible, and it is not used as much as it could be used. For example, if there’s no screen to conveniently retrieve the most commonly asked question on a chat, you may never know it.

有时您必须在另一个工具中以另一种形式重新输入相同的知识。例如,修复错误的提交可能有一条注释,表明它修复了错误,但在许多公司中,您必须转到工作跟踪器来声明您已经修复了错误。您还必须声明在任务上花费的时间,然后才能以聚合形式再次将其输入到时间跟踪工具中。这是浪费时间。考虑将这些工具集成在一起。

Sometimes you have to re-enter the same knowledge in another form in another tool. For example, a commit to fix a bug may have a comment that states it fixed the bug, but in many companies, you have to go to the work tracker to declare that you’ve fixed the bug. You also have to declare the time spent on the task, only to enter it again into the time tracking tool later in an aggregated form. This is a waste of time. Consider integrating the tools together.

工具之间更好的集成还有助于简化人工任务,从而减少手动记录任务的需要。但是,当集成失败时,您确实需要文档。理想情况下,集成组件应该提供此文档。例如,集成脚本应该是可读的并且尽可能具有声明性。

Better integration between tools also helps simplify human tasks, which reduces the need for manual documentation of the tasks. However, when an integration fails, you do need documentation. Ideally, the integration component should provide this documentation. For example, an integration script should be readable and as declarative as possible.

因此:利用工具中存储的知识。决定什么工具对于每一个知识点来说是唯一的权威。搜索可以提供与其他工具或特定报告集成的插件以用于您的文档目的。了解如何使用每个工具的命令行界面以编程方式提取知识或将各种工具与其他工具集成。发现工具提供的 API,包括电子邮件或聊天集成。

Therefore: Exploit the knowledge stored in your tools. Decide what tool is the unique authority for each bit of knowledge. Search for plugins that can provide integration with other tools or specific reports for your documentation purposes. Learn how to use the command-line interface for each of your tools to programmatically extract knowledge or integrate various tools with other tools. Discover the APIs provided by the tools, including the email or chat integration.

作为最后的手段,了解如何查询工具的内部数据库,但要注意数据库可能随时更改,恕不另行通知,因为它通常不是官方 API 的一部分。

As a last resort, find out how to query the internal database of a tool, but beware that a database may change at any time without prior notice, as it’s usually not part of the official API.

以下是一些工具及其知识的示例:

The following are some examples of tools and their knowledge:

  • 源代码控制:带有命令的 Git 等工具blame可以告诉您谁更改了什么以及何时更改,显示您提交的评论,并显示拉取请求讨论。

  • Source control: Tools such as Git with the blame command can tell you who changed what and when, show you commit comments, and reveal pull request discussions.

  • 内部聊天系统: Slack 等系统可以揭示问题、启动构建、发布信息、提及词语、活动、情绪、人物和时间。

  • Internal chat system: Systems such as Slack can reveal questions, launch build, release information, mentions of words, activity, moods, who, and when.

  • 用户目录邮件列表:这些工具可以列出团队、团队成员和团队经理,这可以帮助您了解要联系谁来获取支持、要联系谁来升级问题等等。

  • User directory mailing lists: These tools can list teams, team members, and team managers, which helps you know who to contact for support, who to contact for escalation, and so on.

  • 控制台历史记录:此类工具可以告诉您最近或最常用的命令或命令序列。

  • Console history: Such tools can tell you the most recently or frequently used commands or sequences of commands.

  • 服务注册表:此工具可以为您提供每个正在运行的服务、其地址以及任何其他标签的列表。

  • Services registry: This tool can give you a list of every running service, its address, and any additional tags.

  • 配置服务器:该工具可以为您提供环境配置详细信息。

  • Configuration server: This tool can give you environment configuration details.

  • 公司服务目录:此目录列出了服务治理信息,例如联系对象、上次更新时间等。

  • Company service catalog: This catalog lists services governance information, such as who to contact, time last updated, and so on.

  • 项目注册表:即使是共享驱动器上的电子表格文件也可以告诉您项目名称、代码、领导者、赞助商标识、预算代码等。

  • Project registry: Even a spreadsheet file on a shared drive can tell you project names, codes, leader, sponsor identification, budget codes, and so on.

  • 声纳组件:这些工具可以跨多个存储库和多种技术以不同的详细程度显示逻辑单元、指标及其趋势的分组。

  • Sonar components: These tools can show groupings into logical units, metrics, and their trends at various levels of details and across multiple repositories and multiple technologies.

  • 项目跟踪工具历史记录或发布管理工具历史记录:这些工具可以告诉您有关更改的信息,包括更改者、更改时间和当前版本。

  • Project tracking tools history or release management tools history: These tools can tell you about changes, including who, what when, and current versions.

  • 电子邮件服务器:这些工具通常用于存档以下内容以进行审核(例如,通过转发到存档地址):手动报告、手动决策(例如上线决策)以及最有知识的协作者。

  • Email server: These tools are commonly used to archive the following for auditing (for example, by forwarding to the archiving address): manual reports, manual decisions such as go-live decisions, and the most knowledgeable collaborators.

概括

Summary

大多数(但不是全部)有价值的知识已经以某种形式存在于系统的工件中。通向活文档的道路始于承认已经存在的各种权威知识来源。它还涉及确定是否存在可以提取到各种文档中的单一事实来源,或者是否存在需要协调机制的冗余来源。如果知识分散在许多地方,您可能需要一种整合机制将其重新整合在一起。

Most, but not all, valuable knowledge already exists in some form in the artifacts of your system. The path to living documentation starts with acknowledging the various authoritative sources of knowledge that are already present. It also involves determining whether there is a single source of truth, which can be extracted into various kinds of documents, or whether there are redundant sources that require a reconciliation mechanism. If the knowledge is spread over many places, you may need a consolidation mechanism to put it back in one piece.

大多数知识已经存在,但不是 100%,这表明您需要找到方法用缺失的知识来丰富或增强系统本身,使其成为知识完整的。这是下一章的主题。

Most knowledge is already there, but not 100% of it, which suggests that you need to find ways to enrich, or augment, the system itself with the missing knowledge so that it becomes knowledge complete. That is the topic of the next chapter.

第4章

Chapter 4

知识扩充

Knowledge Augmentation

源代码可能包含从未执行的代码、谎言的变量和过程名称,并且通常是了解程序员意图的糟糕方法。对我来说,设计既是决策和决策的结果,也是决策的原因。有时代码会清楚地表明这一点,但通常情况下不会。

The source code might have code that is never executed, variable and procedure names that are lies, and is in general a poor way to learn the programmer’s intention. To me, design is as much the decisions and the reasons as the results of the decisions. Sometimes code makes that clear, but usually it doesn’t.

—拉尔夫·约翰逊,http://c2.com/cgi/wiki?WhatIsSoftwareDesign

—Ralph Johnson, http://c2.com/cgi/wiki?WhatIsSoftwareDesign

软件是根据其源代码构建的。这是否意味着源代码告诉了应用程序生命周期中需要了解的所有信息?当然,源代码说明了很多信息——而且必须如此。源代码描述了如何构建软件,以便编译器可以做到这一点。干净的代码更进一步,旨在让其他开发人员尽可能清晰地了解知识。

Software is built from its source code. Does this mean that the source code tells everything there is to know over the lifecycle of the application? Sure, the source code tells a lot—and it has to. The source code describes how to build the software so that the compiler can do it. Clean code goes further, aiming to make knowledge as clear as possible for the other developers working on it.

尽管如此,代码通常还是不够的。当代码不能讲述完整的故事时,您需要添加缺失的知识以使其完整。

Still, code is often not enough. When the code doesn’t tell the full story, you need to add the missing knowledge to make it complete.

当编程语言不够用时

When Programming Languages Are Not Enough

大多数编程语言没有预定义的方式来声明关键决策、记录理由以及解释针对所考虑的替代方案所做的选择。编程语言永远无法告诉一切。他们专注于他们的关键范例,并依靠其他机制来表达其余部分:命名、注释、库等等。

Most programming languages have no predefined way to declare the key decisions, to record the rationale, and to explain the choice made against the considered alternatives. Programming languages can never tell everything. They focus on their key paradigm and rely on other mechanisms to express the rest: naming, comments, libraries, and so on.

一座桥的隐喻

A Bridge Metaphor

这里将建造一座桥梁作为一个隐喻。一座桥梁是根据其技术图纸建造的。然而,如果在某个时候需要用更坚固的材料(例如钢材)更换木材,那么原始的技术图纸是不够的。他们会告诉为木料选择的尺寸,但他们不会告诉尺寸来自哪里。他们不会透露与材料的阻力、材料的疲劳、或抵抗强水和极端风力相关的计算。他们不会说出在绘制图纸时什么被认为是“极端”的。也许现在需要重新考虑设计,以根据最近发生的事件适应更极端的条件。也许在这座桥最初建造的时候,

Consider the construction of a bridge as a metaphor here. A bridge is built based on its technical drawings. However, if at some point it is necessary to replace its wood timbers with new ones in a stronger material, such as steel, the original technical drawings wouldn’t be enough. They would tell the dimensions chosen for the wooden timbers, but they wouldn’t tell where the dimensions came from. They wouldn’t tell about the calculations related to the resistance of materials, or fatigue of materials, or resistance against strong waters and extreme wind forces. They wouldn’t tell what was considered “extreme” at the time the drawings were made. Perhaps the design needs to be reconsidered now, to accommodate more extreme conditions in light of recent events. Perhaps at the time the bridge was originally built, no one though a tsunami would be possible in this place, but now we know it could actually happen.

当涉及到记录设计决策及其基本原理时,编程语言除了简单的标准决策(例如成员或继承的典型可见性)之外没有多大帮助。

When it comes to documenting design decisions and their rationale, programming languages can’t help much beyond simple standard decisions such as the typical visibility of members or inheritance.

当一种语言不支持设计实践时,命名约定等变通办法通常可以完成这项工作。一些无法表达私有方法的语言会在它们前面加上下划线。没有对象的语言采用的约定是第一个函数参数称为this。然而,即使使用最好的编程语言,开发人员头脑中的很多想法仍然无法仅用该语言完全表达。

When a language does not support a design practice, workarounds such as naming conventions can often do the job. Some languages with no way to express private methods prefix them with an underscore. Languages without objects adopt a convention of having a first function parameter called this. Yet even with the best programming language, a lot of what’s in the developer’s head still cannot be fully expressed by the language alone.

可以将知识添加为代码注释。但注释缺乏结构,除非你劫持像 Javadoc 这样的结构化注释。此外,重构并不适用于注释,也适用于代码。

It’s possible to add knowledge as code comments. But comments lack structure, unless you hijack structured comments like Javadoc. Also, refactoring does not apply to comments as well as it applies to code.

因此:增强您的编程语言,以便代码能够以结构化的方式讲述完整的故事。定义您自己的方式来声明每个关键决策背后的意图和推理。声明更高层次的设计意图、目标和理由。

Therefore: Augment your programming language so that the code can tell the full story, in a structured way. Define your own way to declare the intentions and the reasoning behind each key decision. Declare the higher-level design intentions, the goals, and the rationales.

不要依赖简单的评论。使用强命名约定或使用语言的扩展机制,例如Java注释和.Net属性;越结构化越好。请毫不犹豫地专门为此文档目的编写一些代码。创建您的领域特定语言 (DSL) 或根据需要重复使用一种语言。在合适的时候依赖约定。

Don’t rely on plain comments for this. Use strong naming conventions or use the extension mechanisms of the language, such as Java annotations and .Net attributes; the more structured, the better. Don’t hesitate to write a little code solely for this documentation purpose. Create your domain-specific language (DSL) or reuse one if needed. Rely on conventions when suitable.

使增强的知识尽可能接近与其相关的代码。理想情况下,它们应该搭配在一起以提供完全重构的证据。让编译器检查是否有任何错误。依赖 IDE 的自动完成功能。确保增强知识可以在编辑器或 IDE 中轻松搜索,并确保可以通过工具轻松解析它,以从整个增强代码中提取实时文档。

Keep the augmented knowledge as close as possible to the code it is related to. Ideally, they should be collocated to be totally refactoring proof. Have the compiler check for any errors. Rely on the autocompletion of the IDE. Make sure the augmented knowledge is easily searchable in your editor or IDE and ensure that it can be easily parsed by tools to extract living documents out of the whole augmented code.

增强代码包含许多对未来维护者有价值的提示。添加与代码相关的知识时,一个重要的考虑因素是代码更改时知识如何演变。代码会改变,因为它就是这样的。因此,附加知识必须保持准确或与代码同时更改,而无需或很少进行手动维护。重命名类或包时会发生什么?删除一个类后会发生什么?你想要添加的额外知识应该是重构证明。

Augmented code contains a lot of valuable hints for the future maintainers. One important consideration when adding knowledge related to the code is how it evolves when the code changes. Code will change because that’s the way it is. As a consequence, it’s essential for the additional knowledge to either remain accurate or change at the same time as the code, with no or very little manual maintenance. What happens when a class or package is renamed? What happens when a class is deleted? The extra knowledge you want to add should be refactoring proof.

增强代码非常适合在代码中明确决策以及添加决策背后的基本原理。

Augmented code is great for making decisions explicit in the code and for adding the rationale behind the decisions.

由于它是结构化的,因此增强的代码也很容易在 IDE 中搜索和导航,无需插件。这意味着它也可以以另一种方式工作:根据所选的基本原理,您可以找到与其相关的所有代码。这对于可追溯性或影响分析非常有价值。

Because it is structured, augmented code is also easy to search and to navigate in the IDE, without plugins. This means that it also works the other way: From a chosen rationale, you can find all the code that is related to it. This is quite valuable for traceability or impact analysis.

实践中的增强代码可以通过多种方法来完成:

Augmented code in practice can be done with several approaches:

  • 内部文档

    • 通过注释

    • 按照惯例

  • Internal documentation

    • By annotation

    • By convention

  • 外部文档

    • 带边车文件

    • 具有元数据数据库

    • 使用DSL

  • External documentation

    • With sidecar files

    • With a metadata database

    • With a DSL

使用注释的文档

Documentation Using Annotations

通过使用注释来扩展编程语言以用于文档目的是我最喜欢的用 Java 或 C# 等语言增强代码的方法。注释不会对命名或代码结构施加任何限制,这意味着它们适用于大多数代码库。因为它们的结构与编程一样语言本身,可以依靠编译器来防止错误,并依靠 IDE 进行自动完成、导航和搜索。

Extending a programming language by using annotations for documentation purposes is my favorite way to augment code in languages such as Java or C#. Annotations do not impose any constraint on naming or code structure, which means they work in most code bases. And because they are as structured as the programming language itself, it’s possible to rely on the compiler to prevent errors and to rely on the IDE for autocompletion, navigation, and searching.

注释的主要优点是它们易于重构:当它们所附加的元素被重命名时,它们是健壮的;当元素移动时,它们会随之移动;当元素被删除时,它们也会被删除。这意味着即使代码发生很大变化,也无需付出额外的努力来维护它们。

The main strength of annotations is that they are refactoring-friendly: They are robust when the element they are attached to is renamed, they move with it when it moves, and they get deleted when it’s deleted. This means no extra effort to maintain them, even if the code changes a lot.

因此:使用结构化注释解释设计及其目的。创建、增长和维护预定义注释的目录,然后只需包含这些注释即可丰富类、方法和模块的语义。

Therefore: Explain the design and its purpose using structured annotations. Create, grow, and maintain a catalog of predefined annotations, and then simply include these annotations to enrich the semantics of the classes, methods, and modules.

然后,您可以创建一些小工具来利用注释中的附加信息,例如强制约束或将知识提取为另一种格式。

You can then create little tools that can exploit the additional information in the annotations, such as to enforce constraints or to extract knowledge into another format.

当您有注释并且了解它们时,您可以更快地声明设计决策:只需添加注释即可。注释就像已经发生的想法的书签(见图4.1)。

When you have annotations and you know them, you can more quickly declare a design decision: Just add the annotation. Annotations are like bookmarks for the thinking that has happened (see Figure 4.1).

图示“增强代码=代码+注释”的图显示了代码加书签(代表注释)。

图 4.1 增强代码 = 代码 + 注解

Figure 4.1 Augmented Code = Code + Annotations

注释可以表示类构造型,例如值、实体、领域服务和领域事件。它们可以代表活跃的模式协作者,例如复合材料或适配器。他们可以声明编码风格和默认首选项。

Annotations can represent class stereotypes such as values, entities, domain services, and domain events. They can represent active pattern collaborators, such as composites or adapters. They can declare styles of coding and the default preference.

重要的是,您的注释应尽可能与具有标准名称的标准技术相对应。如果您需要自己的自定义文件,请务必将它们记录在每个人都知道的地方。

It’s important that your annotations correspond to standard techniques with standard names as much as possible. If you need your own custom ones, be sure to document them in a place that everybody knows.

通过添加注释来声明您根据标准知识和标准实践做出的决定,可以鼓励刻意练习。您必须知道自己在做什么,并且必须知道行业文献中的名称。使用标准设计模式和注释可以减少完成任务所需的时间。

Placing an annotation to declare your decisions in terms of standard knowledge and standard practices encourages deliberate practice. You have to know what you’re doing, and you have to know what it’s called in the industry literature. Using standard design patterns and annotations can reduce the time required to complete a task.

还可以在 IDE 中搜索注释,这很方便。例如,您可以搜索由选定注释注释的每个类,这为您提供了一种导航设计的新方法。

Annotations are also searchable in the IDE, which is handy. For example, you can search for every class that is annotated by a selected annotation, which gives you a new way to navigate the design.

结构化注释是一个强大的工具,但它们可能不足以完全取代所有其他形式的文档来描述所有设计决策及其意图。您仍然需要每个相关人员之间进行对话。此外,一些知识和见解最好通过清晰的写作来解释细微差别的感觉——这在注释中很难做到。您可能还发现需要跟踪更细微的方面,例如决策中涉及的情绪,例如恐惧、品味、厌恶、政治压力。其他媒体(例如纯文本)更适合这一点。

Structured annotations are a powerful tool, but they are probably not enough to completely replace all other forms of documentation to describe all design decisions and their intentions. You still need conversations between everyone involved. In addition, some knowledge and insights are best explained through clear writing with a sense of nuance—something that’s hard to do in annotations. You may also find it desirable to keep track of more nuanced aspects like emotions involved in decision-making, such as fears, taste, distaste, political pressure. Other media, such as plain text, are better for that.

最后,使用注释声明的知识是机器可读的,这意味着工具可以利用这些知识来帮助团队。例如,动态图表和动态词汇表就依赖于这种可能性。想象一下您可以做什么——或者您可以让工具为您做什么——使用可以理解您的设计意图的工具!

Finally, the knowledge declared using annotations is machine readable, which means tools can exploit this knowledge to help the team. Living diagrams and living glossaries, for example, rely on such possibilities. Imagine what you could do—or what you could have tools do for you—with tools that can understand your design intents!

注释不仅仅是标签

Annotations as More Than Tags

Java 中的注释和 .Net 中的属性是其编程语言的真正公民。它们有一个名称和一个模块名称(包或命名空间)。它们还保存参数,并且本身可以由其他注释进行注释。而且因为它们是类,所以它们还受益于 Javadoc 等文档生成器使用的结构化注释语法。所有这一切意味着您可以通过简单的注释传达大量知识。

Annotations in Java and attributes in .Net are genuine citizens of their programming language. They have a name and a module name (package or namespace). They also hold parameters and can themselves be annotated by other annotations. And because they are classes, they also benefit from the structured comments syntax used by documentation generators like Javadoc. All this means you can convey a lot of knowledge through simple annotations.

让我们看一个技术示例。注释使用元注释来描述它们的应用位置。例如,这里的注释Adapter可以应用于类型和包:

Let’s look at a technical example. Annotations use meta-annotations to describe where they can be applied. For example, here the annotation Adapter can be applied to types and packages:

1 @Target({ ElementType.TYPE, ElementType.PACKAGE })
2 公共@interface适配器{
3 }
1  @Target({ ElementType.TYPE, ElementType.PACKAGE })
2  public @interface Adapter {
3  }

以下示例涉及带有参数的注释。如果要注释构建器模式的实例,则可以将构建器生成的类型描述为注释的参数:

The following example involves annotations with parameters. If you were to annotate an instance of a builder pattern, you could describe the type that the builder produces as a parameter of the annotation:

1 公共@interface Builder {
2 Class[] products() 默认 {};
3 }
4
5 @Builder(产品 = {Supa.class, Dupa.class})
6 公共类 SupaDupaBuilder {
7 //...
8}
1  public @interface Builder {
2  Class[] products() default {};
3  }
4
5  @Builder(products = {Supa.class, Dupa.class})
6  public class SupaDupaBuilder {
7  //...
8  }

通常,声明的返回类型和实现的接口已经可以讲述很多类似的信息,但它们错过了附加注释传达的精确语义。事实上,更精确的注释为更多自动化打开了大门,因为它们为工具提供了一种用更高级别的语义解释源代码的方法。

Often the declared return types and implemented interfaces can already tell a lot of similar information, but they miss the precise semantics that additional annotations convey. In fact, more precise annotations open the door to more automation because they give tools a way to interpret the source code with higher-level semantics.

正如语义网旨在将非结构化数据转换为数据网络一样,带有澄清源代码语义的注释的代码库也成为机器可以解释的数据网络。

Just as the Semantic Web aims to transform unstructured data into a web of data, a code base with annotations that clarify the semantics of the source code becomes a web of data that machines can interpret.

描述决策背后的理由

Describing the Rationale Behind Decisions

值得为子孙后代记录的最重要的信息之一是每个决定背后的理由。几年后看起来可能是一个愚蠢的选择,但当它做出决定时并不那么愚蠢。最重要的是,当理由是指某个时间点的上下文,而现在上下文不同时,您现在可以更好地重新考虑该决定。

One of the most important pieces of information worth recording for future generations is the rationale behind each decision. What may seem like a stupid choice years later was not so stupid when it was decided. Most importantly, when the rationale is referring to a context at some point in time, and now the context is different, you are in a better position to reconsider the decision now.

例如,假设很久以前就选择了一个昂贵的数据库,因为它是少数能够在内存中完全缓存数据的数据库之一。现在阅读此基本原理,您可能会考虑使用 NoSQL 数据存储来实现此目的。再举一个例子,假设应用程序的各个层都通过 XML 相互通信,这会让您的生活变得很麻烦并导致性能问题。这一决定的基本原理是,该架构旨在物理分布在各层之间,以便进行扩展。然而,多年后,很明显这永远不会发生,所以您现在知道您可以消除所有额外的复杂性。如果没有明确的理由,你总是会怀疑自己是否错过了什么,并且不敢重新考虑整个事情。

For example, say that an expensive database was chosen long ago because it was one of the few to be able to fully cache data in memory. Reading this rationale now, you may consider instead using NoSQL datastores for that purpose. As another example, say that an application has layers talking to each other through XML everywhere, which makes your life cumbersome and causes performances issues. The rationale for this decision explains that this architecture was meant to be distributed physically between layers in order to scale. However, after many years, it has become clear that this will never happen, so you know now that you could remove all the extra complexity. Without a clear rationale, you would always wonder if you had missed something, and you would not dare reconsidering the whole thing.

嵌入式学习

Embedded Learning

将更多知识放入代码中可以帮助维护人员在工作时学习。至少,注释本身应该被记录下来。如果您有一个名为 的注释Adapter,它的注释应该解释它Adapter是什么。我最喜欢的方法是链接到清晰的在线定义,例如相应的维基百科页面,以及评论本身中的简短文本描述:

Putting more knowledge into code helps its maintainers learn while working on it. At a minimum, annotations should themselves be documented. If you have an annotation named Adapter, its comments should explain what Adapter is. My favorite way to do this is to link to a clear online definition, such as the corresponding Wikipedia page, along with a brief text description within the comment itself:

1   /** 
2   * 适配器模式是一种软件设计模式,
允许另一个类使用现有类的
3   * 接口
界面。
4   * 
5   * 适配器包含它包装的类的实例,并且
6   * 将调用委托给包装对象的实例。
7   * 
8   * 参考:参见 <a href="http://en.wikipedia.org/wiki/\
9 * Adapter_pattern"> Adapter_pattern </a>
10 */ 
11  公共@接口适配器{
12}
1  /**
2  * The adapter pattern is a software design pattern that
allows the
3  * interface of an existing class to be used from another
interface.
4  *
5  * The adapter contains an instance of the class it wraps, and
6  * delegates calls to the instance of the wrapped object.
7  *
8  * Reference: See <a href="http://en.wikipedia.org/wiki/\
9  * Adapter_pattern">Adapter_pattern</a>
10 */
11  public @interface Adapter {
12  }

这比看起来更重要。从现在开始,带有此注释的每个类都只需一个工具提示即可获得其设计角色的完整文档。

This is more important than it might seem. From now on, every class with this annotation is only a tooltip away from complete documentation of its design role.

Adapter考虑项目中随机类的示例,在本例中位于 RabbitMQ 中间件之上:

Consider the example of a random Adapter class in a project, in this case on top of the RabbitMQ middleware:

1 @适配器
2 公共类RabbitMQAdapter {
3   //... 
4 }
1 @Adapter
2 public class RabbitMQAdapter {
3  //...
4 }

当在任何 IDE 中打开此类时,当鼠标悬停在其上时,工具提示会显示其文档,如图4.2所示。

When this class is opened in any IDE, when the mouse hovers over it, the tooltip displays its documentation, as shown in Figure 4.2.

该图显示了注释“适配器”的工具提示,其中显示了其文档。

图4.2 注释的工具提示显示其文档

Figure 4.2 The tooltip of the annotation displays its documentation

工具提示描述提供了简短的解释,但对于已经了解该信息但只需要复习一下的开发人员特别有用。如果需要比工具提示提供的更多信息,可以单击链接以重定向到更多信息。他们可能会在此过程中提出问题,但至少有一个简单的学习切入点。在本例中,注释描述了该类Adapter是适配器模式的实例,并且它们充当了解有关适配器模式的更多信息的门户。

The tooltip description provides a brief explanation but is especially useful for developers who already know the information but just need a refresher. Those who need more information than the tooltip provides can click the link to be redirected to more information. They will probably ask questions in the process, but at least there’s an easy entry point to the learning. In this case, the annotations describe that the Adapter class is an instance of the adapter pattern, and they act as a gateway to learning more about the adapter pattern.

因此:将更多的知识放入代码中不仅仅是为了文档;它还可以帮助有意识地提高从事该工作的团队的技能。在决定增强代码策略时请考虑这个机会。扩充代码时,请考虑您的同事发现它时会如何反应。

Therefore: Putting more knowledge into the code is not just for documentation; it can also help deliberately increase the skills of the teams working on it. Consider this opportunity when deciding on your augmented code strategy. When augmenting code, think about how your colleagues will react when they discover it.

注释还可以链接到最能解释该主题的一本书或几本书。或者它可以链接到公司的电子学习计划。

An annotation could also link to the book or books that best explain the topic. Or it could link to a company e-learning program.

作为在评论中包含链接的替代方案,同一本书中的每个注释都可以有一个代表该书的元标签。在下面的示例中,AdapterDecorator注释都代表来自《四人帮》一书《设计模式》GoF的设计模式,有关该书的信息可以包含在专门与该书相关的元注释中:

As an alternative to including links in comments, every annotation from the same book could have a meta-tag representing the book. In the following example, both Adapter and Decorator annotations represent design patterns from the Gang of Four book Design Patterns, and information about the book can be included in a meta-annotation GoF specifically about the book:

1   /** 
2   * 书籍: <a href="http://books.google.fr/books/about/Design_
3 Patterns.html?id=6oHu\KQe3TjQC">Google 图书</a>
4   */
5 @Target(ElementType.ANNOTATION_TYPE)
6 公共@interface GoF {
7}
8
9 @GoF
10 公共@interface适配器{
11}
12
13 @GoF
14 公共@interface装饰器{
15}
1  /**
2  * Book: <a href="http://books.google.fr/books/about/Design_
3  Patterns.html?id=6oHu\KQe3TjQC">Google Book</a>
4  */
5  @Target(ElementType.ANNOTATION_TYPE)
6  public @interface GoF {
7  }
8
9  @GoF
10 public @interface Adapter {
11 }
12
13 @GoF
14 public @interface Decorator {
15 }

这只是一个示例,当然您不仅限于记录设计模式!请随意根据这些想法详细阐述您自己的组织知识的计划。

This is only an example, and of course you are not limited to documenting design patterns! Feel free to elaborate your own scheme for organizing your knowledge based on these ideas.

在评论中使用结构化标签

如果您使用的编程语言没有注释,则可以在注释中使用结构化标签:

If you are using a programming language that does not have annotations, you can use structured tags within comments:

1 /** @适配器 */
1 /** @Adapter */

在这种情况下,遵循结构化文档的通用风格是个好主意。该语言可能提供一些工具支持,例如自动完成或代码突出显示。XDoclet 库在早期的 Java 时代取得了巨大成功,劫持了 Javadoc 标签以便将它们用作注释。

It’s a good idea in this case to conform to a common style of structured documentation. The language may provide some tool support, such as autocompletion or code highlighting. The XDoclet library did that with great success in the early Java days, hijacking the Javadoc tags in order to use them as annotations.

您还可以使用良好的旧标记接口模式,其中涉及实现一个不带方法的接口来标记类。例如,要将一个类标记为可序列化,您可以实现该Serializable接口:

You may also use the good old marker interface pattern, which involves implementing an interface with no method in order to mark the class. For example, to mark a class as serializable, you implement the Serializable interface:

1 公共类 MyDto 实现可序列化 {
2 ...
3 }
1  public class MyDto implements Serializable {
2    ...
3  }

请注意,这是一种相当侵入性的标记类的方法,它会污染类型层次结构,但它很好地说明了我们在这里讨论的内容。

Note that this is quite an intrusive way to tag a class, and it pollutes the type hierarchy, but it provides a good illustration of what we’re discussing here.

当注释太过时

When Annotations Go Too Far

Google Annotations Gallery 1是一个于 2010 年退役的开源项目,它提出了一系列简洁的注释,以通过您的设计决策、意图、诚实的感受甚至羞耻感来增强您的代码。

Google Annotations Gallery1 is a retired open-source project from 2010 that proposed a collection of neat annotations to augment your code with your design decision, intentions, honest feelings, and even shame.

1. Google 注释库, https://code.google.com/p/gag/

1.Google Annotations Gallery, https://code.google.com/p/gag/

发现愚蠢的代码?您可以留下@LOL@Facepalm@WTF注释:

Discovering stupid code? You can leave a @LOL or @Facepalm or @WTF annotation:

1 @捂脸
2 if(找到== true){...}
1  @Facepalm
2  if(found == true){...}

或者你可以给所有这些留下一个解释:

Or you can leave all of them with an explanation:

1 @LOL @Facepalm @WTF(“只需使用 Collections.reverse()”)
2 <T> void invertOrdering(List<T> 列表) {...
1  @LOL @Facepalm @WTF("just use Collections.reverse()")
2  <T> void invertOrdering(List<T> list) {...

你还可以使用remark注解来抢先限定你自己的惨代码:

You can also use remark annotations to preemptively qualify your own miserable code:

1 @Hack 公共字符串
2 unescapePseudoEscapedCommasAndSemicolons(String url) {
1  @Hack public String
2  unescapePseudoEscapedCommasAndSemicolons(String url) {

......或者证明它的合理性:

…or to justify it:

1 @BossMadeMeDoIt
2 字符串 extractSQLRequestFromFormParameter(字符串
参数){...}
1 @BossMadeMeDoIt
2 String extractSQLRequestFromFormParameter(String
params){...}

您可以使用注释警告您的团队成员@CantTouchThis

You can warn your team members with the @CantTouchThis annotation.

偶然发现某种无法正常工作的代码?人生苦短。标记它并@Magic继续:

Stumble across code that somehow works beyond all reason? Life’s short. Mark it with @Magic and move on:

1 @Magic public static int negate(int n) {
2 返回 new Byte((byte) 0xFF).hashCode()
3 / (int) (短) '\uFFFF' * ~0
4 * 字符.数字('0', 0)
5  * n * (整数.MAX_VALUE * 2 + 1) / (字节.MIN_VALUE >> 7)
6 1 * (~1 | 1);
7}
1 @Magic public static int negate(int n) {
2  return new Byte((byte) 0xFF).hashCode()
3  / (int) (short) '\uFFFF' * ~0
4  * Character.digit ('0', 0)
5  * n * (Integer.MAX_VALUE * 2 + 1) / (Byte.MIN_VALUE >> 7)
6  1 * (~1 | 1);
7 }

而当你做好设计后,你就可以用文学注释让世界知道你的才华:

And when you’ve done a good job of design, you can let the world know your brilliance with the literary annotations:

1 @Metaphor 公共接口 Life 扩展 Box { }
1 @Metaphor public interface Life extends Box { }

或者:

or:

1 @Oxymoron 公共接口 DisassemblerFactory {
反汇编器
2 创建反汇编器(); }
1  @Oxymoron public interface DisassemblerFactory {
Disassembler
2  createDisassembler(); }

按惯例记录文件

Documentation by Convention

使用简单的约定来记录您的决定很方便。例如,在Java中,每个以大写字母开头的标识符都是一个类,每个以小写字母开头的标识符都是变量名。

Using plain conventions to document your decisions is convenient. For example, in Java, every identifier that starts with an uppercase letter is a class, and every identifier that starts with a lowercase letter is a variable name.

许多技术中都有适用于许多情况的约定,并且您始终可以在任何技术环境之上添加自己的约定,无论是代码、XML、JSON、程序集还是 SQL。即使是使用旧技术的旧项目也依赖约定来交流知识、描述其结构并帮助导航。

There are conventions for many situations, in many technologies, and you can always add your own conventions on top of any technical environment, be it code, XML, JSON, assembly, or SQL. Even old projects with old technologies rely on conventions to communicate knowledge, describe their structure, and help with navigation.

以下是一些按照惯例的文档示例:

Here are some examples of documentation by convention:

  • 按层的包名称: named 包中的所有内容都*.domain.*可能代表域逻辑,而named 包中的所有内容*.infra.*可能代表基础设施代码。

  • Package names by layer: Everything in a package named *.domain.* might represent domain logic, whereas everything in a package named *.infra.* might represent infrastructure code.

  • 按技术类构造型命名的包名称:在许多代码库中,将每个数据访问对象类分组到以缩写命名的包中是很常见的*.dao.*;类似地,对于包中的 Enterprise Java Bean*.ejb.*以及包中您最喜欢的框架使用的普通旧 Java 对象*.pojo.*

  • Package names by technical class stereotype: It’s common in many code bases to group every data access object class in a package named after the abbreviations *.dao.*; similarly for your Enterprise Java Beans in a package *.ejb.* and your plain old Java objects used by your favorite framework in a package *.pojo.*.

  • 提交注释:您可以使用诸如 之类的约定[FIX] issue-12345 free text,其中方括号将提交类型分类为FIXREFACTORFEATURECLEAN,并issue-xxx引用错误跟踪器中的票证 ID。

  • Commit comments: You could use a convention such as [FIX] issue-12345 free text, where the square brackets categorize the type of commit as either FIX, REFACTOR, FEATURE, or CLEAN, and issue-xxx references the ticket ID in the bug tracker.

  • Ruby on Rails 风格的约定优于配置:在此约定中,如果数据库表被命名为orders,则控制器将被命名为orders_controller

  • The Ruby on Rails style of convention over configuration: In this convention, if a database table is named orders, the controller would be named orders_controller.

带有约定的遗留代码中的活文档

Living Documentation in Legacy Code with Conventions

每当您拥有遵循约定的现有代码库时,您就有机会通过利用所有现有约定来走活文档路线,甚至无需接触源代码来添加任何内容。(这对于注释文档来说是不可能的。)

Whenever you have an existing code base that follows conventions, you have an opportunity to go the living documentation route by exploiting all the existing conventions, without even having to touch the source code to add anything. (This is not possible with documentation by annotation.)

例如,假设现有应用程序遵循分层设计。如果幸运的话,它的包名称通过命名约定直接代表各层:

For example, say that an existing application follows a layered design. If you’re lucky, its package names represent the layers directly through naming conventions:

1 /记录存储目录/gui
2 /记录存储目录/businesslogic
3 /记录存储目录/dataaccesslayer
4 /记录存储目录/数据库模式
1  /record-store-catalog/gui
2  /record-store-catalog/businesslogic
3  /record-store-catalog/dataaccesslayer
4  /record-store-catalog/db-schema

您的文档已经存在于 Java 包的命名中或者 C# 中的命名空间或子项目中。

Your documentation is already there in the naming of the Java packages or in namespaces or subprojects in C#.

记录约定

Documenting the Conventions

如果团队中的每个人都熟悉所使用的约定,那么您不需要任何进一步的文档。其他公司发布的约定称为现成文档,您可以采用此类约定,然后只需在 README 文件中创建对约定集的外部文档的引用即可。但在实践中,我建议始终在自述文件中记录约定。这是在真实代码库中记录约定的示例:

If everyone on a team is familiar with the conventions used, then you don’t need any further documentation. Conventions published by another company are called ready-made documentation, and you can adopt such conventions and then just create a reference to the external documentation of the sets of conventions in the README file. In practice, though, I recommend always documenting the conventions in the README file. Here’s an example of documenting conventions in a real code base:

1 自述文件.txt
2
3 该应用程序遵循分层架构。
4 每一层都有自己的包,命名如下
5个约定:
6
7 /图形用户界面/*
8 /业务逻辑/*
9 /数据访问层/*
10 /db-模式/*
11
12 GUI层包含有关图形用户的所有代码
13个接口。
14 所有负责显示和数据输入的代码都必须存在。
15
16 业务逻辑层包含所有特定领域的
17 逻辑和行为。这就是领域模型所在的地方。
18 业务逻辑应该只存在于此处,而不应存在于其他地方。
19
20 数据访问层包含所有的DAO(Data Access
21个对象)负责与数据库交互。
22 存储技术的任何变化只会影响
23 这一层,没有其他层(至少理论上是这样:)
24
25 DB Schema 包含所有要设置、删除的 SQL 脚本
26或更新数据库。
27
28 重要规则:每一层只能依赖于该层
29 以下。任何层都不能依赖于上面的一个或多个层,
30 这是禁止的!
1 README.txt
2
3 This application follows a Layered Architecture.
4 Each layer has its own package, with the following naming
5 conventions:
6
7 /gui/*
8 /businesslogic/*
9 /dataaccesslayer/*
10 /db-schema/*
11
12 The GUI layer contains all the code about the graphical user
13 interface.
14 All code responsible for display & data entry must be there.
15
16 The business logic layer contains all the domain-specific
17 logic and behavior. This is where the domain model is.
18 Business logic should only be there and nowhere else.
19
20 The data access layer contains all the DAO (Data Access
21 Objects) responsible to interact with the database.
22 Any change of storage technology should only impact
23 this layer and no other layer (in theory at least :)
24
25 The DB Schema contains all the SQL scripts to setup, delete
26 or update the database.
27
28 Important Rule: Each layer can only depend on the layers
29 below. No layer can depend on the layer or layers above,
30 this is forbidden!

有些约定是有代价的,特别是当它们给命名带来干扰时。例如,在标识符上添加前缀或后缀(例如,VATCalculation-Service, DispatchingManager, DispatchingDTO)是一种标准做法,但这不是干净的代码,并且代码中的名称不再属于业务领域语言!

Some conventions carry a cost, especially when they add noise to the naming. For example, putting prefixes or suffixes on identifiers (for example, VATCalculation-Service, DispatchingManager, DispatchingDTO) is a standard practice, but it’s not clean code, and the names in your code do not belong to the business domain language anymore!

当包中的每个接口都是服务时,添加Service前缀不会添加任何信息,只会添加噪音。如果包中的每个类都是冗余信息,则/dto/可能不需要后缀。DTO

When every interface in a package is a service, adding the Service prefix adds no information, just noise. Every class in a /dto/ package might not need the DTO suffix if it would be redundant information.

一贯遵守惯例

Consistently Adhering to Conventions

只有当每个参与人员都有足够的纪律来始终遵守约定时,按惯例编写的文档才有效。编译器不关心您的约定,并且对强制执行它们没有太大帮助。

Documentation by convention works only to the extent that everyone involved has enough discipline to adhere to the conventions consistently. The compiler does not care about your conventions and won’t help much in enforcing them.

一处拼写错误,你就已经不遵守惯例了!当然,您可以调整编译器或 IDE 解析器,也可以使用静态分析工具来检测某些违反约定的情况。有时需要做很多工作,但有时却出奇的简单,所以你可以尝试一下。

One typo, and you’re already not following the convention! You can, of course, tweak the compiler or your IDE parser, or you can use static analysis tools to detect some violations of conventions. Sometimes it’s a lot of work, but other times it’s surprisingly easy, so you may give it a try.

依靠约定的文档来帮助生成活文档(例如动态图表)会鼓励并奖励遵循约定:如果您违反约定,那么您的活文档将会失败,这很好。

Relying on documentation by convention to help produce living documents such as living diagrams encourages and rewards following the conventions: If you break the convention, then your living documents will fail, which is nice.

约定的局限性

The Limitations of Conventions

约定对于对代码段进行分类非常有效,但是当您尝试用附加知识(例如基本原理、替代方案等)来丰富它们时,它们很快就会显示出其局限性。相比之下,注释可能能够更好地包含此类附加知识。

Conventions work well for categorizing sections of code, but they quickly show their limits when you try to enrich them with additional knowledge such as rationales, alternatives, and so on. In contrast, annotations may be better able to include such additional knowledge.

约定通常只不过是供人类使用的自由文本。但是,您仍然可以获得一些针对您的约定的工具支持:

Conventions are often little more than free text meant for humans. However, you may still get some tool support for your conventions:

  • 您可以使用每个约定的模板来配置 IDE。例如,您可以输入几个字符并让模板正确打印全名以遵守约定;对于具有更复杂约定的提交注释,模板可能会打印一个您可以填写的占位符。

  • You can configure your IDE with templates for each convention. For example, you might be able to type a few characters and have the template print the full name properly to adhere to the convention; for a commit comment with a more complicated convention, the template might print a placeholder that you can just fill in.

  • 您可以让您的活文档生成器解释约定来执行其工作。

  • You can have your living document generators interpret the conventions to perform their work.

  • 您可以根据命名约定(例如,使用 JDepend、ArchUnit 或您自己构建在任何代码解析器之上的工具)强制执行规则,例如层之间的依赖关系。

  • You can enforce rules such as dependencies between layers based on the naming conventions (for example, using JDepend, ArchUnit, or your own tool built on top of any code parser).

与注释相比,约定还具有不打乱旧习惯的优点。如果您的团队和经理非常保守,您可能更喜欢按约定路线查看文档,而不是按注释路线查看文档。您可能会猜到我更喜欢通过注释来记录文档。

Compared to annotations, conventions also have the advantage of not disrupting old habits. If your team and managers are very conservative, you may prefer going the documentation by convention route rather than the documentation by annotation route. You can probably guess that I prefer documentation by annotation.

外部文档方法

External Documentation Methods

按注释的文档和按约定的文档是直接出现在代码本身中的内部文档形式。相反,以下各节中描述的技术是外部文档的形式,因为它们驻留在远离所记录的事物的位置。

Documentation by annotation and documentation by convention are forms of internal documentation that appear right into the code itself. In contrast, the techniques described in the following sections are forms of external documentation, as they reside at a location that is remote from the thing being documented.

边车文件

Sidecar Files

当无法将注释放入代码中时,您可以将它们放入代码旁边的文件中。Sidecar 文件(也称为伙伴文件、伴随文件或连接文件)是存储源文件格式无法支持的元数据的文件。对于每个源文件,通常有一个关联的 sidecar 文件,其名称相同但文件扩展名不同。

When putting annotations within code is not possible, you can put them into a file next to the code. Sidecar files—also known as buddy files, companion files, or connected files—are files that store metadata that cannot be supported by the source file format. For each source file, there is typically one associated sidecar file with the same name but a different file extension.

例如,某些 Web 浏览器将网页保存为一对文件:一个 HTML 文件和一个同名但带有 _folder 前缀的 sidecar 文件夹。作为另一个示例,数码相机可能能够在拍照时记录一段音频,并且相关联的音频可以存储为与 .jpg 文件同名的 sidecar 文件,但扩展名为 .wav。

For example, some web browsers save a web page as a pair of files: an HTML file and a sidecar folder of the same name but with a _folder prefix. As another example, a digital camera might have the capability to record a piece of audio while taking a picture, and the associated audio may be stored as a sidecar file with the same name as the .jpg file, but with a .wav extension.

Sidecar 文件是一种外部注释。它们可用于添加任何类型的信息,例如分类标签或自由文本注释,而无需触及文件系统上的原始源文件。

Sidecar files are a type of external annotations. They can be used to add any kind of information, such as a classification tag or a free text comment, without having to touch the original source file on the file system.

使用 sidecar 文件的主要问题是,当文件管理器不知道源文件与其 sidecar 文件之间的关系时,它无法阻止用户仅重命名或移动其中一个文件而不移动另一个文件,并且该关系是破碎的。因此,我不建议使用 sidecar 文件,除非你别无选择。

The main problem with using sidecar files is that when the file manager is not aware of the relationship between the source file and its sidecar file, it cannot prevent the user from renaming or moving only one of the files without the other, and the relationship is broken. For this reason, I don’t recommend using sidecar files unless you have no other choice.

笔记

Note

旧的源代码控制系统(例如并发版本系统(CVS))使用了大量的 sidecar 文件。

Old source control systems like Concurrent Versions System (CVS) used a lot of sidecar files.

元数据数据库

Metadata Databases

当无法在代码中添加注释时,您可以将它们保存在外部数据库中。元数据数据库是存储引用其他源文件或组件的元数据的数据库。一个众所周知的例子是 iTunes 数据库,其中包含许多与每首歌曲相关的元数据(例如,播放列表、最近的收听历史记录),但这些元数据不适合歌曲的音频文件。元数据可能不适合该文件,因为该文件格式没有地方存储元数据,或者根本不应该更改该文件。

When putting annotations within code is not possible, you can keep them in an outside database. A metadata database is a database that stores metadata referencing other source files or components. A well-known example is the iTunes database, which contains a lot of metadata associated with each song (for example, playlists, recent listening history) that doesn’t fit within the song’s audio file. The metadata might not fit within the file because the file format does not have a place to store the metadata or because it would not be a good idea to change the file at all.

元数据也可能引用文件,但实际上并不是文件固有的,因此它应该存储在其他地方。例如,照片不应存储有关其属于相册的一部分的信息;最好把专辑存放在某个地方别的。以类似的方式,照片缩略图的 URL 是仅照片应用程序感兴趣的元数据,通过在照片文件自己的结构中包含此类元数据来破坏照片文件将是侵入性的(假设它甚至是可能的)。

Metadata may also reference a file but not really be intrinsic to it, and hence it should be stored somewhere else. For example, a photo should not store information about its being part of an album; it would be better to store the album somewhere else. In a similar fashion, the URL of the thumbnail of a photo is metadata that is of interest only for the photo application, and it would be intrusive to corrupt the photo file by including that kind of metadata in its own structure (assuming it were even possible).

使用元数据数据库进行注释的主要问题,就像使用 sidecar 文件的主要问题一样,如果元数据数据库和相应文件之一在未经授权的情况下被重命名、移动或删除,则很容易导致元数据数据库和相应文件不同步。数据库正在更新。

The main problem with using metadata databases for annotations, just like the main problem with using sidecar files, is that it’s easy for the metadata database and the corresponding files to get out of sync if one of these files is renamed, moved, or deleted without the database being updated.

当根本无法触及文件并且元数据必须存储在其他地方时,元数据数据库应该是最后的选择。然而,当元数据的管理由不同的人而不是管理文件本身的人同时对所有文件进行批量管理时,这也是一种方便的方法。例如,如果摄影师管理数百张照片,但元数据数据库是由图书馆员管理的普通电子表格,那么图书馆员可以轻松快速地将所有元数据添加到列中,这要归功于复制/粘贴,现代电子表格应用程序的插值和计算能力。摄影师不必参与,也不存在错误损坏照片文件的风险。

A metadata database should be the last-resort choice when it’s not possible to touch the files at all and metadata has to be stored elsewhere. However, it is also a convenient approach when the management of the metadata is done in bulk across all files at the same time, by different people than the people managing the files themselves. For example, if hundreds of photos are managed by a photographer, but the metadata database is a plain spreadsheet managed by a librarian, then it is easy  for the librarian to quickly add all the metadata in a column, thanks to the copy/paste, interpolation, and calculation abilities of a modern spreadsheet application. The photographer does not have to be involved, and there is no risk of corrupting the photo file by mistake.

元数据数据库的常见示例是发现注册表中嵌入的各种键/值存储;部署、配置和供应工具;服务目录;为注册表添加书签;等等。每当您可以引用某些内容并添加标签时,您就拥有了一个事实上的元数据数据库!

Common examples of metadata databases are the various key/value stores embedded in discovery registries; deployment, configuration, and provisioning tools; service catalogs; bookmarking registries; and so on. Whenever you can reference something and add tags, you have a de facto metadata database!

设计自定义注释

Designing Custom Annotations

无论公司、部门或大陆如何,现成的文献对于快速学习他人的经验和共享通用词汇至关重要。然而,此类文献的问题在于,为了与所有人共享,它必须放弃针对每个特定上下文的特定内容。

Off-the-shelf literature is essential in quickly learning from the experience of others and sharing a common vocabulary, whatever the company, department, or continent. However, the problem with such literature is that in order to be shared with everyone, it has to give up what is specific to each particular context.

您应该使用这个标准的知识体系,并且您还可以扩展它以使其更具表现力。您可以通过添加和扩展从标准文献中发展您的标签和注释词汇表,使其更适合您自己的上下文。

You should use this standard body of knowledge, and you can also extend it to make it even more expressive. You can evolve your vocabulary of tags and annotations from the standard literature with additions and extensions to make it more specific to your own context.

例如,我们或多或少都同意六种颜色的标准圆圈,但在您自己的视觉宪章中,您当然会使用这些颜色的自定义变体,这些颜色是特定于您的。你的浅蓝色当然是蓝色,但由你来定义“光”的含义。

For example, we more or less all agree on a standard circle of six colors, but in your own visual charter, you certainly use custom variants of these colors, which are specific to you. Your light blue is certainly a blue, but it is up to you to define what “light” means.

刻板属性

Stereotypical Properties

当我们设计代码时,我们会考虑工作行为以及属性(无论是否理想)。以下是一些理想属性的示例:

When we design code, we think in terms of working behavior and also in terms of properties, desirable or not. Here are some examples of desirable properties:

  • NotNull:对于不能为空的参数。当您几乎总是使用它时,生活会变得更加轻松!

  • NotNull: For a parameter that cannot be null. Life is so much easier when you use it almost always!

  • Positive:对于必须为正的参数。

  • Positive: For a parameter that has to be positive.

  • Immutable:对于保持不变的类。

  • Immutable: For a class that remains unchanged.

  • Identity by value:其中平等被定义为数据的平等。

  • Identity by value: Where equality is defined as the equality of data.

  • Pure:对于一个函数或一个类的每个函数以避免副作用。

  • Pure: For a function or for every function of a class to avoid side effects.

  • Idempotent:对于多次调用时具有相同效果的函数(这在分布式系统中非常重要)。

  • Idempotent: For a function that has the same effect when called more than once (which is very important in distributed systems).

  • Associative:对于诸如 (a + b) + c = a + (b + c) 之类的函数,在执行 map-reduce 类操作时。

  • Associative: For a function such as (a + b) + c = a + (b + c), when doing map-reduce kinds of things.

当你使用这些属性时,你需要在代码中明确你的用法。只要有可能,您就可以使用类型系统来完成此操作。例如,如果它内置于语言中或由标准库提供,则Option可以表达没有结果的可能性。Optional使用 Scala案例类本身就是(Immutable, Identity by value)的简写。如果无法做到这一点,您可以使用注释或自定义注释以及自动化测试和基于属性的测试来表达属性。

When you use these properties, you need to make your usage clear in the code. You can do this with the type system whenever possible. For example, it is possible to express the possibility of having no result with Option or Optional if it is built in to the language or provided by a standard library. Using a Scala case class is in itself a shorthand for (Immutable, Identity by value). When this is not possible, you can express the properties with comments or with custom annotations, along with automated tests and property-based testing.

刻板印象和战术模式

Stereotypes and Tactical Patterns

在 Java 或 C# 等语言中,一切都是类,但并非每个类都属于同一类型或具有相同的用途。请注意,在函数式编程语言中,一切都是函数,但并非每个函数都有相同的用途。领域驱动设计提出了一些基本的类类别,例如值对象实体领域服务领域事件。它还建议借鉴其他模式,例如使用设计模式(例如,策略和复合模式)。关键是一些(但不是全部)设计模式也是领域模式。

In a language like Java or C#, everything is a class, but not every class is of the same kind or has the same purpose. Note that in functional programming languages, everything is a function, but not every function has the same purpose either. Domain-driven design proposes some fundamental categories of classes, such as value object, entity, domain service, and domain event. It also suggests borrowing from other patterns, such as using design patterns (for example, strategy and composite patterns). The point is that some (but not all) design patterns are also domain patterns.

有趣的是,这些类别的类提供了表达大量信息的压缩方式。例如,当我说类FueldCardTransaction是一个值对象时,我的意思是它的身份仅由它的值定义,它是不可变的,它应该没有任何副作用,并且它应该是可转移的。因此,很自然地明确声明这些模式作为编写文档的简单方法。

It is interesting that these categories of classes offer compressed ways to express a lot of information. For example, when I say that the class FueldCardTransaction is a value object, I mean that its identity is only defined by its values, that it is immutable, that it should be without any side effects, and that it should be transferable. It is therefore natural to declare explicitly these patterns as a simple way to do documentation.

您可以在项目中引入一组自定义注释,如下所示:

You could introduce a set of custom annotations like the following into a project:

  • @ValueObject

  • @ValueObject

  • @Entity@DomainEntity(为了防止所有技术框架中相似名称的注释出现任何歧义)

  • @Entity or @DomainEntity (to prevent any ambiguity with the annotations of similar names from all the technical frameworks)

  • @DomainService

  • @DomainService

  • @DomainEvent

  • @DomainEvent

您可以使用属性显式声明后果。

And you can declare the consequences explicitly by using the properties.

每个类别的类都带有预定义的属性。例如,值对象应该具有值标识、不可变并且没有副作用。您可以通过使用注释上的注释轻松地在注释系统中明确这一点,如下所示:

Each category of class comes with predefined properties. For example, a value object should have identity by value, should be immutable, and should be side effect free. You can easily make this explicit in a system of annotations by using annotations on annotations, as shown here:

1 @不可变
2 @SideEffectFree
3 @IdentityByValue
4 公共@interface ValueObject {
5 ...
1  @Immutable
2  @SideEffectFree
3  @IdentityByValue
4  public @interface ValueObject {
5  ...

当您将一个类标记为值对象时,您也可以使用元注释间接标记它。这是一种将属性分组到捆绑包中的便捷方法,只需一个等效声明即可声明所有属性。当然,bundle应该有明确的名称和含义;它不应该只是一堆随机的属性。

When you mark a class as being a value object, you indirectly mark it with the meta annotations as well. This is a convenient way to group properties that go together into bundles, to declare them all with only one equivalent declaration. Of course, a bundle should have a clear name and meaning; it should not be just a random bunch of properties together.

这种方法可以额外实施设计和架构。例如@DomainEntity@DomainService、 和@DomainEvent暗示是域模型的一部分,并且可能对允许的依赖项进行相关限制,这些都可以通过静态分析来强制执行。

This approach enables additional enforcement of the design and architecture. For example @DomainEntity, @DomainService, and @DomainEvent imply being part of a domain model and perhaps related restrictions on the allowed dependencies, which can all be enforced with static analysis.

正如本章稍后所述,您可以在 Java 中的包上添加注释,以便一个位置的声明共同标记包的每个元素。您可以以“除非另有说明”的方式利用这一点。例如,您可以定义一个名为 的自定义注释@FunctionalFirst,该注释将放置在整个包上,这意味着默认情况@Immutable@SideEffect-Free适用于每种类型,除非在特定类型上明确声明了其他内容。

As described later in this chapter, you can put annotations on packages in Java so that a declaration in one place collectively marks every element of the package. You can take advantage of this in an “unless specified otherwise” fashion. For example, you could define a custom annotation named @FunctionalFirst, meant to be put on whole packages, which would mean @Immutable and @SideEffect-Free by default for every type, unless something else were stated explicitly on a particular type.

还有许多其他感兴趣的模式和原型目录,可以有效地表达大量设计和建模知识。它们提供与开发人员工作、设计以及建模和解决基础设施问题相关的现成知识和词汇。但您可以更进一步,将标准类别扩展到更细粒度的类别。

There are many other catalogs of patterns and stereotypes of interest to express efficiently a lot of design and modeling knowledge. They provide ready-made knowledge and vocabulary related to your job as a developer, about design, and about modeling and solving infrastructure problems. But you can go further and extend the standard categories into finer-grained categories.

例如,可以细化值对象的种类。Martin Fowler 写了关于数量模式、空对象模式、特殊情况模式和范围模式的文章,这些都是值对象的特殊情况。另外,货币模式是数量模式的特例。您可以使用所有这些模式,并选择最具体的一种。例如,如果适用,您可以选择范围而不是值对象,因为众所周知,范围是值对象。如果这样做,您可以明确表示范围是值对象的特殊情况,并且在注释上带有注释:

For example, it is possible to refine the kind of value object. Martin Fowler wrote about the quantity pattern, the null object pattern, the special case pattern, and the range pattern, which are all specialized cases of value objects. In addition, the money pattern is a special case of the quantity pattern. You can use all these patterns, choosing the most specific one possible. For example, you could chose range over just value object if it applies, as it is common knowledge that a range is a value object. If you did this, you could make it explicit that a range is a special case of a value object with an annotation on the annotation:

1 @ValueObject
2 公共@interface范围{
3 ...
1  @ValueObject
2  public @interface Range {
3  ...

您还可以创建自己的变体。在一个项目中,我有很多价值对象,但它们远不止这些。它们也是策略模式的实例,域模式相当于策略模式。更重要的是,在金融业务领域,我们通常将其称为标准市场惯例。所以我创建了自己的@Convention注释并明确表示它同时是一个值对象和一个策略:

You can also create your own variants. In one project, I had a lot of value objects, but they were more than that. They were also instances of the policy pattern, the domain pattern equivalent of the strategy pattern. More importantly, in the business domain of finance, we would usually call them standard market conventions. So I created my own @Convention annotation and made it clear that it was at the same time a value object and a policy:

1 @ValueObject
2 @政策
3 公共@interface约定{
4 ...
1  @ValueObject
2  @Policy
3  public @interface Convention {
4  ...

使用有意义的注释包名称

Using Meaningful Annotation Package Names

创建自定义注释时,必须选择其包名称。您可以选择具有特定含义的包名称。我喜欢在包名称中编码一个想法的参考位置。例如,当注释取自一本书时,我可能会使用书名或书名或作者的缩写,例如 com.acme.annotation.gof 代表四人帮的书,com.acme.annotation.gof 代表四人帮的书。acme.annotation.poeaa 用于《企业应用程序架构模式》一书,com.acme.annotation.ddd 用于《领域驱动设计》一书。对于没有一本金书的标准知识,我可能会根据字段命名包(例如,com.acme.annotation.algebra)。

When you create a custom annotation, you have to choose its package name. You can choose for a package name to have a particular meaning. I like to encode a reference place for an idea in the package name. When the annotation is drawn from a book, for example, I might use the book name or an abbreviation of the book name or authors, such as com.acme.annotation.gof for a book by the Gang of Four, com. acme.annotation.poeaa for the book Patterns of Enterprise Application Architecture, and com.acme.annotation.ddd for the book Domain-Driven Design. For standard knowledge with no one golden book, I might name the package after the field (for example, com.acme.annotation.algebra).

劫持标准注释

Hijacking Standard Annotations

Java 世界中的许多框架都使用注释作为配置形式。例如,JPA(Java Persistence API)和 Spring Framework 提供了 XML 和注释之间臭名昭著的选择。尽管我提倡将注释用于文档目的,但我并不热衷于使用注释来替代编写代码。我更喜欢一些 .Net 项目(例如 Fluent NHibernate)中使用纯代码定义对象到关系映射的方法。

Many frameworks in the Java world use annotations as a form of configuration. For example, JPA (Java Persistence API) and Spring Framework offer the infamous choice between XML and annotations. Even though I advocate using annotations for documentation purposes, I am not a big fan of using annotations as an alternative to writing code. I prefer the approach found in some .Net projects, such as Fluent NHibernate, of using plain code to define the object-to-relational mappings.

然而,此时在 Java 中,您仍然必须使用注释,除非您更喜欢 XML(我不喜欢)。当您使用注释来驱动框架的行为时,注释实际上是代码,并且由于它们中的大多数与持久性或 Web 服务等基础设施问题相关,因此它们通常有一个恼人的习惯,即用非域噪声污染域类。

However, in Java at this time, you still have to use annotations, unless you prefer XML (which I do not). When you use annotations to drive the behavior of frameworks, the annotations are code indeed, and since most of them relate to infrastructure concerns such as persistence or web service, they often have the annoying habit of polluting domain classes with non-domain noise.

除了我的小抱怨之外,您可能想知道这些标准注释是否有任何文档价值。因为它们至少是代码,所以注释记录了它们正在做什么——就像设计良好的代码一样。他们讲述什么

Aside from my little rant, you probably wonder whether these standard annotations have any documentation value. Because they are code, at a minimum, the annotations document what they are doing—just like well-designed code. They tell the what.

让我们考虑一些特定文档兴趣的示例:

Let’s consider a few examples of particular documentation interest:

  • 刻板化注释(Spring):这组注释包括@Service@Repository@Controller。它们用于构造类,您可以声明它们以注册到依赖项注入机制中。事实上,它们给注释起@Component了更有意义的别名,这是一种很好的方式来劫持这些嘈杂的注释,以获得对人类更有意义的东西,而不仅仅是对 Spring 更有意义。

  • Stereotyping annotations (Spring): This set of annotations includes @Service, @Repository, and @Controller. They are used to stereotype classes, and you can declare them for registration into the dependency injection mechanism. In fact, they alias the @Component annotation with more meaning, which is a nice way to hijack these noisy annotations for something more meaningful for humans rather than just for Spring.

  • 创建自定义构造型 (Spring):此方法还支持您自己的自定义注释,前提是您使用@Component元注释对它们进行注释。

  • Creating custom stereotypes (Spring): This approach also supports your own custom annotations, provided that you annotate them with the @Component meta annotation.

  • @Transactional(Spring):注释@Transactional用于声明事务边界和规则,通常在服务上。如果您有六边形架构,那么事务服务应该是位于域模型之上的自己的薄层中的应用程序服务。因此,您可以确定这个 Spring 注释本身也具有@Application-ServiceDDD 意义。因为大多数 Spring 注释也是元注释,所以您实际上可以定义自己的@ApplicationService注释并将其标记为,@Transactional以便以 Spring 也可以识别并发挥其魔力的方式表达您的意图。

  • @Transactional (Spring): The @Transactional annotation is used to declare transactional boundaries and rules, typically on a service. If you have a hexagonal architecture, the transactional services should be your application services in their own thin layer on top of the domain model. You could thus decide that this Spring annotation in itself also means @Application-Service in the DDD sense. Because most Spring annotations are also meta annotations, you could actually define your own @ApplicationService annotation and mark it as @Transactional in order to express your intent in a way that Spring can recognize for doing its magic, too.

  • @Inheritance(JPA):注释@Inheritance及其朋友可用于直接记录有关如何在类层次结构和相应的数据库模式之间进行映射的设计决策。这与 Martin Fowler 的《企业应用程序架构模式》一书中的相应模式直接相关。例如,@Inheritance(strategy=JOINED)对应于单表继承模式2(但不幸的是,使用了另一个名称)。

    2. Martin Fowler,“单表继承”,ThoughtWorks,http: //martinfowler.com/eaaCatalog/singleTableInheritance.html

  • @Inheritance (JPA): The @Inheritance annotation and its friends can be used to directly document design decisions on how to do the mapping between a class hierarchy and a corresponding database schema. This directly relates to the corresponding patterns from Martin Fowler’s book Patterns of Enterprise Application Architecture. For example, @Inheritance(strategy=JOINED) corresponds to the single-table inheritance pattern2 (but, unfortunately, under another name).

    2.Martin Fowler, “Single Table Inheritance,” ThoughtWorks, http://martinfowler.com/eaaCatalog/singleTableInheritance.html

  • RESTful Web 服务(JAX-RS 注释):这组注释具有明确的声明性:@Path标识 URI 路径、@GET声明GET请求方法并将@Produces媒体类型定义为参数。生成的代码在很大程度上是自我记录的。此外,Swagger 等工具可以利用这些注释来生成 API 的实时文档。

  • RESTful web service (JAX-RS annotations): This set of annotations is clearly declarative: @Path identifies the URI path, @GET declares the GET request method, and @Produces defines the media type as a parameter. The resulting code is self-documented to a large extent. Furthermore, tools like Swagger can exploit these annotations to generate living documentation of the API.

可以依赖标准注释来获取其特定的文档价值,但这几乎总是限于技术问题,其中注释就像特定的声明性代码一样,它告诉了“内容”,但不告诉为什么。正如前面提到的,有时可以扩展标准机制来传达额外的含义,同时仍然可以与您所依赖的框架很好地配合。

It is possible to rely on the standard annotations for their particular documentation value, but this is almost always limited to technical concerns, where the annotation is just like particularly declarative code in that it tells the what but not the why. As mentioned earlier, it is sometimes possible to extend the standard mechanism to convey additional meaning while still playing nicely with the frameworks you depend on.

标准注解:@Aspect 和面向方面的编程

Standard Annotation: @Aspect and Aspect-Oriented Programming

Spring Pet Clinic 通过展示如何设置一个简单的方面来监视每个存储库的调用计数和调用调用时间,从而演示了面向方面的编程 (AOP) 。3

The Spring Pet Clinic demonstrates aspect-oriented programming (AOP) by showing how to set up a simple aspect that monitors call count and call invocation time for every repository.3

3. https://github.com/spring-petclinic/spring-framework-petclinic/blob/master/src/main/java/org/springframework/samples/petclinic/util/CallMonitoringAspect.java

3.https://github.com/spring-petclinic/spring-framework-petclinic/blob/master/src/main/java/org/springframework/samples/petclinic/util/CallMonitoringAspect.java

这里有趣的是,“监视每个存储库”的需求在切面声明中按字面意思描述,如以下使用@AspectSpring AOP 注释的摘录所示。

It is interesting here that the requirement “to monitor every repository” is described literally in the aspect declaration, as shown in the following excerpt annotated with the @Aspect annotation from Spring AOP.

1 @方面
2 公共类CallMonitoringAspect {
3 ...
4 @Around("在(@org.springframework.stereotype.Repository *)")
5    公共对象调用(ProceedingJoinPoint joinPoint)抛出
可投掷{
6 ...
7}
8 ...
9 }
1  @Aspect
2  public class CallMonitoringAspect {
3    ...
4    @Around("within(@org.springframework.stereotype.Repository *)")
5    public Object invoke(ProceedingJoinPoint joinPoint) throws
Throwable{
6      ...
7    }
8  ...
9  }

这种表达能力是可能的,因为代码已经通过有意义的@Repository构造型得到了增强。这完美地说明了如何通过明确的设计决策来增强代码,从而能够以我们人类的思维方式与工具对话。

This expressiveness is possible because the code has been augmented with the meaningful @Repository stereotype. This illustrates perfectly how augmenting the code with explicit design decisions makes it possible to talk to tools the way we humans think.

默认注释或除非必要

Annotation by Default or Unless Necessary

在设计自定义注释来表达属性时,您可以选择在满足或不满足属性时为情况创建注释:

When designing custom annotations to express properties, you have a choice of creating an annotation for the case when the property is met or when it’s not met:

  • @Immutable或者@Mutable

  • @Immutable or @Mutable

  • @NonNull或者@Nullable

  • @NonNull or @Nullable

  • @SideEffectFree或者@SideEffect

  • @SideEffectFree or @SideEffect

您可以创建两者并让个人决定选择哪一个,但这可能会造成不一致,在这种情况下注释根本没有任何意义。

You may create both and let individuals decide which one to choose, but that might create inconsistencies, in which case the annotation means nothing at all.

您可以决定要推广的替代方案,以便在许多地方添加注释成为营销活动;例如,使用@NonNull“Everywhere”将鼓励将所有内容设为非空。没有注释则表明可为空。

You may decide on an alternative that you want to promote so that having the annotation in many places becomes a marketing campaign; for example, using @NonNull everywhere will encourage making everything non-null. No annotation then suggests nullability.

另一方面,您可能会认为注释是噪音,并认为注释越少越好。在这种情况下,默认且首选的选择应该是无注释,并且您将仅使用注释来声明与默认值的偏差。如果团队偏好默认情况下每个类都是不可变的,那么您将注释可变类,因为您希望同事注意到:“哦,这个类非常出色@Mutable!”

On the other hand, you might consider that annotations are noise and think that the fewer annotations the better. In this case, the default and preferred choice should be no annotation, and you would use an annotation only to declare a deviation from the default. If the team preference is to have every class immutable by default, you would annotate mutable classes because you want your colleagues to notice: “Oh, this class is exceptionally @Mutable!”

处理模块范围的知识

Handling Module-Wide Knowledge

在软件项目中,模块包含一组可以一起操作的工件(本质上是包、类和嵌套模块)。您可以定义适用于模块包含的所有元素的属性。设计特性和质量属性要求(例如,只读、可序列化、无状态)通常适用于整个模块,而不仅仅是模块内的不同元素。

In a software project, a module contains a set of artifacts (essentially packages, classes, and nested modules) that can be manipulated together. You can define properties that apply to all the elements a module contains. Design properties and quality attribute requirements (for example, being read only, serializable, stateless) often apply on a whole module, not just on distinct elements within the module.

您还可以在模块级别定义主要编程范例:面向对象、函数式、甚至过程式或报告式。

You can also define the primary programming paradigm at the module level: object-oriented, functional, or even procedural or reporting style.

模块也是声明架构约束的理想选择。例如,您可以为具有高质量标准的从头编写的代码和具有更宽容标准的遗留代码划分不同的区域。在每个模块中,您可以定义样式首选项,例如 Checkstyle 配置、指标阈值、单元测试覆盖率以及允许或禁止的导入。

A module is also ideal for declaring architecture constraints. For example, you could have distinct areas for code written from scratch with high-quality standard and for legacy code with more tolerant standards. In each module, you could define preferences of style such as Checkstyle configuration, metrics thresholds, unit test coverage, and allowed or forbidden imports.

因此:当一个知识在一个模块内均匀地跨越多个工件时,您应该直接将该知识放在模块级别,以便它适用于所有包含的元素。

Therefore: When a piece of knowledge spans a number of artifacts equally within a module, you should put this knowledge at the module level directly, so that it applies to all the contained elements.

这种方法也可以应用于满足给定谓词的所有元素,只要您可以找到该声明的归宿,例如面向方面编程中的切入点。

This approach can also be applied to all elements satisfying a given predicate, as long as you can find a home for this declaration, such as the pointcuts in aspect-oriented programming.

处理多种模块

Dealing with Many Kinds of Modules

包是 Java 和其他语言中最明显的模块。但包 xyz 实际上定义了多个模块:其直接成员的模块 ( x.y.z.*) 以及还包括其子包中包含的每个工件的模块 ( x.y.z.**)。类似地,类也代表其成员字段、方法和嵌套类的“模块”,例如x.y.z.A#x.y.z.$

Packages are the most obvious modules in Java and in other languages. But a package x.y.z actually defines more than one module: the module of its direct members (x.y.z.*) and the module that also include every artifact included in its subpackages (x.y.z.**). Similarly, a class also represents a “module” for its member fields, methods, and nested classes—for example, x.y.z.A# and x.y.z.$.

像 Eclipse 这样的 IDE 中的“工作集”还定义了另一个逻辑分组,类似于模块,作为类和其他资源的简单集合。Ant 等工具还使用文件列表和正则表达式定义文件集,例如{x.y.z.A, x.y.z.B, x.y.*.A}. 与模块一样,工作集和文件集通常被命名以方便参考。

The “working sets” in an IDE like Eclipse also define another logical grouping, similar to modules, as simple collections of classes and other resources. Tools like Ant also define filesets using lists of files and regular expressions—for example, {x.y.z.A, x.y.z.B, x.y.*.A}. Like modules, working sets and filesets are usually named for easy reference.

源文件夹(例如,src/main/java 或 src/test/java)显然定义了粗粒度的元素分组。Maven 模块以子项目的规模定义更大的模块。面向方面编程的切入点还定义了跨各种“真实”模块的逻辑分组元素。

Source folders (for example, src/main/java or src/test/java) obviously define coarse-grained groupings of elements. Maven modules define bigger modules, at the scale of subprojects. Pointcuts of aspect-oriented programming also defines logical groupings elements across various “real” modules.

继承和实现也隐式定义模块,例如“类的每个子类或接口的实现”为x.y.z.A+,如果它包含每个嵌套成员的每个成员,则它是x.y.z.A++

Inheritance and implementation implicitly define modules, too, such as “every subclass of a class or implementation of an interface” as x.y.z.A+, and if it includes every member of every nested member, it is x.y.z.A++.

构造型隐式定义了其出现的集合。例如,值对象模式隐式定义了作为值对象的每个类的逻辑集。

A stereotype implicitly defines the set of its occurrences. For example, the value object pattern implicitly defines the logical set of every class that is a value object.

模型-视图-控制器 (MVC) 和知识级别等协作模式还意味着逻辑分组,例如 MVC 的模型部分或知识级别模式的每个级别(知识级别或操作级别)。

Collaboration patterns such as Model–View–Controller (MVC) and knowledge level also imply logical groupings such as the model part of MVC or each level of the knowledge level pattern (knowledge level or operational level).

设计模式还根据模式中所扮演的角色来定义逻辑分组(例如,“抽象工厂模式中的每个抽象角色”都是@AbstractFactory.Abstract.*)。

Design patterns also define logical groupings by the role played within the pattern (for example, “Every abstract role in the Abstract Factory pattern” would be @AbstractFactory.Abstract.*).

层、域、有界上下文和聚合根等概念隐含了许多其他模块或准模块。

There are many other modules or quasi-modules implied by concepts like layers, domains, bounded contexts, and aggregate roots.

大型模块的问题在于它们包含大量项目,这通常需要进行积极的过滤,甚至可能需要进行排名以仅考虑更多元素中的N个最重要的元素。

The problem with large modules is that they contain huge numbers of items, which often necessitates aggressive filtering and may even require ranking to consider only the N most important elements out of many more.

实践中的模块范围增强

Module-Wide Augmentation In Practice

所有用附加知识来增强代码的技术都适用于模块范围的知识:注释、命名约定、sidecar 文件、元数据数据库和 DSL。

All the techniques to augment code with additional knowledge apply for module-wide knowledge: annotations, naming conventions, sidecar files, metadata database, and DSL.

将文档添加到 Java 包的常见方法是使用名为 package-info.java 的特殊类作为 Javadoc 和有关包的任何注释的位置。请注意,这个具有神奇名称的特殊伪类实际上是 sidecar 文件的示例。

A common way to add documentation to a Java package is to use a special class named package-info.java as a location for the Javadoc and any annotation about the package. Note that this special pseudo-class with a magic name is actually an example of a sidecar file.

C# 模块通常包含项目,其中可以有程序集信息描述:

C# modules often contain projects, which can have assembly information descriptions:

1 AssemblyInfoDescription("包注释")
1  AssemblyInfoDescription("package comment")

在大多数编程语言中,包或命名空间命名约定也可用于声明设计决策。例如,something.domain可用于将包或命名空间标记为域模型。

In most programming languages, package or namespace naming conventions can also be used to declare design decisions. For example, something.domain can be used to mark a package or namespace as a domain model.

内在知识增强

Intrinsic Knowledge Augmentation

警告

Caution

本节比其他大多数部分更加抽象。这里讨论的概念很重要但很微妙。如果抽象的废话绝对不是你的菜,你可以放心地跳过这一部分,也许稍后再回来看。

This section is more abstract than most others. The concepts discussed here are important but subtle. If abstract nonsense is definitely not your thing, you can safely skip this section and perhaps come back to it later.

重要的是要区分事物真正的用途和它们的其他用途或目的。汽车可能是红色的,可能是轿跑车,或者可能有混合动力发动机。这些属性确实是汽车固有的;它们是其身份的一部分。相比之下,汽车的所有者、汽车在某个时间点的位置或汽车在公司车队中的角色对于汽车来说是外在的。这种外在知识实际上并不是关于汽车本身,而是关于汽车与其他事物之间的关系。因此,除了汽车本身之外,它还可能因许多原因而改变。考虑内在知识和外在知识对于设计和文档编制有很多好处。

It is important to make a distinction between what things really are for themselves and what they are for something else or for a purpose. A car may be red, may be a coupe, or may have a hybrid engine. These properties are really intrinsic to the car; they are part of its identity. In contrast, the owner of the car, the car’s location at a point in time, or the car’s role in a company fleet are extrinsic to the car. This extrinsic knowledge is not really about the car in itself but about a relationship between the car and something else. As a consequence, it can change for many reasons other than the car itself. Thinking about intrinsic versus extrinsic knowledge has many benefits for design and for documentation.

如果仅将内在知识附加到元素上,则会发生以下情况:

If only intrinsic knowledge is attached to an element, the following would happen:

  • 如果您要删除该元素,附加的知识将随之消失,不会后悔,也不会在其他任何地方进行修改。例如,当汽车被回收时,它的序列号会同时被处理,这是可以的。

  • If you were to delete the element, the attached knowledge would go away with it, without regret and without modification anywhere else. For example, when the car is recycled, its serial number is crunched at the same time, and this is okay.

  • 本质上与元素无关的任何更改都不会修改该元素或其工件。例如,出售汽车不会修改其用户手册。

  • Any change that it is not intrinsically about the element would not modify the element or its artifacts at all. For example, selling the car would not modify its user manual.

了解外在属性的重要性

Understanding the Importance of Extrinsic Properties

我第一次了解内在与外在的概念是在《四人帮》一书《设计模式》中。介绍轻量级模式的章节考虑了文字处理器中使用的字形。文本中的每个字母都作为字形(字符的渲染图像)打印在屏幕上。字形具有大小和样式属性,例如斜体或粗体。字形在页面上也有一个 (x, y) 位置。轻量级模式背后的核心思想是利用字形的内在属性(例如,其大小、样式)与其外在属性(例如,其在页面上的位置)之间的差异,以实现对同一字形实例的重用。该字形在页面上多次出现。

I first learned about the notion of intrinsic versus extrinsic in the Gang of Four book Design Patterns. The chapter that introduces the lightweight pattern considers a glyph used in a word processor. Each letter in the text is printed on the screen as a glyph, the rendered image of a character. A glyph has a size and style attributes such as italic or bold. A glyph also has an (x, y) position on the page. The core idea behind the lightweight pattern is to exploit the difference between intrinsic properties of the glyph (for example, its size, its style) and its extrinsic properties (for example, its position on the page) to enable reuse of the same instance of the glyph many times on the page.

这个解释对我的设计方式产生了很大的影响。它是提高设计决策的长期相关性的秘密因素。

This explanation has had a big influence on the way I design. It is a secret ingredient in improving the long-term relevance of design decisions.

因此:仅使用元素固有的知识来注释元素。相反,考虑将所有内在知识附加到元素本身。避免附加外在的知识,因为它会经常发生变化,并且由于与元素无关的原因。随着时间的推移,对内在知识的关注将减少文档的维护工作。

Therefore: Only annotate elements with knowledge that is intrinsic to them. Conversely, consider attaching all intrinsic knowledge to the element itself. Avoid attaching knowledge that is extrinsic, as it will change often and for reasons unrelated to the element. A focus on intrinsic knowledge will reduce the maintenance efforts of the documentation over time.

关键

Key Point

您可能会将这种对内在知识的关注视为或多或少明智的耦合问题。关键问题是“当我改变元素时,我所声明的知识将如何演变?” 最好的方法是当元素发生变化时需要您做最少工作的方法。

You may think of this attention to intrinsic knowledge as a matter of more or less judicious coupling. The key question is “How would my declared knowledge have to evolve when I change the element?” The best approach is the one that requires you to do the least work when the element changes.

流行框架对注释的常见使用通常不会考虑它们是否真的是所注释事物的内在特征。例如,假设您有一个本身存在并且可以独立使用的类,但是您在它上面添加了注释以声明它应该如何映射到数据库或声明它是某些的默认实现界面。如果您认为此类确实代表了域职责,那么此数据库映射是一个不相关的问题;附加它只会使类更有可能因数据库原因而更改。

The common use of annotations by popular frameworks regularly does not consider whether they’re really intrinsic to the thing annotated. For example, say that you have a class that exists in itself and that can be used independently, but then you put annotations on it to declare how it is supposed to be mapped to the database or to declare that it is the default implementation for some interface. If you consider this class to really represent a domain responsibility, then this database mapping is an unrelated concern; having it attached only makes the class more likely to change for database reasons, too.

想象一下,您有一个CatalogDAO具有两个实现的接口:MongoDBCatalogDAOPostgresCatalogDAO。将类标记MongoDBCatalogDAO为接口的默认实现CatalogDAO将是强制对类进行外部关注的一个示例。更好的替代方案是使用诸如or 之DAO类的内在属性来注释每个属性,并分别通过此中间属性间接进行选择。例如,您可以使用注释标记所有实现,并使用注释标记所有实现。这是关于@MongoDB@PostgresMongoDBDAO@MongoDBPostgresDAO@PostgresDAO。另外,您可以决定注入为特定部署选择的技术的每个实现。如果您使用 Postgres 进行部署,我们希望注入每个@Postgres实现。注入一项选定技术的决定也是知识,但DAO层次结构不必知道。

Imagine that you have a CatalogDAO interface with two implementations: MongoDBCatalogDAO and PostgresCatalogDAO. Marking the MongoDBCatalogDAO class as the default implementation of the CatalogDAO interface would be an example of forcing an extrinsic concern on the class. A better alternative would be to annotate each DAO with an intrinsic attribute like @MongoDB or @Postgres and separately make the selection indirectly via this intermediate attribute. For example, you could mark all MongoDBDAO implementations with the @MongoDB annotation and all PostgresDAO implementations with the @Postgres annotation. This is intrinsic knowledge with respect to the DAO. Separately, you could decide to inject every implementation for the technology chosen for a particular deployment. If you deploy with Postgres, we want to inject every @Postgres implementation. This decision to inject one selected technology is knowledge, too, but the DAO hierarchy shouldn’t have to know.

机器可访问的文档

Machine-Accessible Documentation

您在设计级别编码,而不仅仅是代码级​​别,但是您的工具在设计级别无法为您提供太多帮助。他们无能为力,因为仅根据代码,他们从设计角度不知道您在做什么。如果您使设计变得明确,例如通过使用附加到代码的注释,那么工具也可以开始在设计级别操作代码,从而为您提供更多帮助。

You code at the design level, not just the code level, but your tools cannot help you much at the design level. They cannot help because they have no idea, based on the code alone, what you are doing from a design perspective. If you make your design explicit, such as by using annotations attached to the code, then tools can begin to manipulate the code at the design level, too, and thus help you more.

能够让代码更加明确的设计知识值得补充。附加到语言元素的注释通常就足够了。例如,您可以在相应的 package-info.java 文件中声明每个顶级包上的层:

Design knowledge that can make the code more explicit is worth adding. An annotation attached to the language element is often enough. For example, you can declare the layers on each top-level package in the corresponding package-info.java file:

1 @Layer(LayerType.INFRASTRUCTURE)
2 包com.example.infrastruct;
1  @Layer(LayerType.INFRASTRUCTURE)
2  package com.example.infrastructure;

通过将注释@Layer放在包 com.example.infrastruct 上,您可以声明层模式的特定实例,其中层是包本身。

By putting the annotation @Layer on the package com.example.infrastructure, you declare a particular instance of the layer pattern, where the layer is the package itself.

与往常一样,设计自定义注释有很多选项,例如声明 ID(这对于稍后引用它可能很有用):

As usual, there are many options for designing a custom annotation, such as declaring an ID (which may be useful for referencing it later):

1 @Layer(id = "存储库")
2 包com.example.domain;
1  @Layer(id = "repositories")
2  package com.example.domain;

通过在代码本身中明确表达这一设计意图,依赖关系检查器等工具可以自动导出层之间禁止的依赖关系,以检测何时违反了这些依赖关系。

With this design intent made explicit in the code itself, tools such as a dependency checker could automatically derive forbidden dependencies between layers to detect when they are violated.

您可以使用 JDpend 等工具来执行此操作,但您必须声明每个包到包的依赖关系限制。这很乏味,并且不能直接描述分层;它只描述了分层的结果。

You could do this with tools like JDpend, but you’d have to declare each package-to-package dependency restriction. This it tedious and does not directly describe the layering; it describes just the consequence of the layering.

声明每个禁止的或可接受的包到包的依赖关系是乏味的,但想象一下在类之间这样做:这是令人望而却步的!但是,如果类被标记为(例如,@ValueObject@Entity@DomainService),依赖项检查器可以强制执行您最喜欢的依赖项限制。例如,我喜欢执行以下规则:

Declaring every forbidden or acceptable package-to-package dependency is tedious, but imagine doing it between classes: It’s prohibitive! However, if classes are tagged—for example, as @ValueObject, @Entity, or @DomainService— dependency checkers can enforce your favorite dependency restrictions. For example, I like to enforce the following rules:

  • 值对象永远不应该依赖于其他值对象以外的任何东西。

  • Value objects should never depend on anything other than other value objects.

  • 实体永远不应该将任何服务实例作为成员字段。

  • Entities should never have any service instance as a member field.

一旦明确地用这些构造型增强了类,您就可以更字面、更简洁地告诉工具您想要什么。

Once the classes are augmented with these stereotypes explicitly, you can more literally and more concisely tell the tools what you want.

文学编程

Literate Programming

让我们改变对程序构建的传统态度:不要想象我们的主要任务是指导计算机做什么,而是让我们集中精力向人类解释我们希望计算机做什么。

Let us change our traditional attitude to the construction of programs: Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do..

——唐纳德·高德纳4

—Donald Knuth4

4.唐纳德·高德纳 (Donald Knuth), http://www.literateprogramming.com

4.Donald Knuth, http://www.literateprogramming.com

在一本关于活文档的书中很难不提到文学编程。文学编程是 Donald Knuth 提出的一种编程方法。一个有文字的程序用自然语言(例如英语)解释程序逻辑,以及宏片段和传统源代码。工具处理程序,生成供人类使用的文档和可以编译的源代码,成为可执行程序。

It is hard not to mention literate programming in a book on living documentation. Literate programming is an approach to programming introduced by Donald Knuth. A literate program explains program logic in a natural language, such as English, along with snippets of macros and traditional source code. A tool processes the program, producing both a document for humans and source code that can be compiled, becoming an executable program.

尽管文学编程从未广泛流行,但它对行业产生了深远而广泛的影响,即使这个想法经常被扭曲。

Although literate programming never became widely popular, it had a profound and widespread influence on the industry, even if the idea was often distorted.

文学编程引入了几个重要的思想:

Literate programming introduced several important ideas:

  • 在相同的工件中,文档与代码交错,代码插入到文档的散文中:这不应与文档生成相混淆,文档生成是从插入源代码的注释中提取的。

  • Documentation interleaved with the code, in the same artifacts, with code inserted within the prose of the documentation: This should not be confused with documentation generation, where the documentation is extracted from comments inserted into the source code.

  • 文档遵循程序员的思维流程,而不是受到编译器强加的顺序的约束:好的文档遵循人类逻辑的顺序。

  • Documentation following the flow of thoughts of the programmer, as opposed to being constrained by the compiler-imposed order: Good documentation follows the order of the human logic.

  • 鼓励程序员认真思考每个决定的编程范式:文学编程远远超出了文档:它旨在迫使程序员认真思考,因为他们必须明确说明程序背后的想法。

  • A programming paradigm encouraging programmers to think deliberately about each decision: Literate programming goes well beyond documentation: It is meant to force programmers to think deliberately, as they have to explicitly state their thoughts behind the program.

请记住,文学编程不是一种编写文档的方法,而是一种编写程序的方法。

Keep in mind that literate programming is not a way to do documentation but a way to write programs.

尽管它的使用并不广泛,但文字编程今天仍然存在,并且有适用于所有优秀编程语言的工具,包括 Haskell、Clojure 和 F#。现在的重点是用 Markdown 编写散文,并插入编程语言的片段。在 Clojure 中,您使用 Marginalia,5在 CoffeeScript 中,您使用 Docco,6在 F# 中,您使用 Tomas Petricek 的 FSharp。格式化。7

Even though its use is not widespread, literate programming is still alive today, with tools available for all good programming languages, including Haskell, Clojure, and F#. The focus now is on writing prose in Markdown, with snippets of programming language inserted. In Clojure you use Marginalia,5 in CoffeeScript you use Docco,6 and in F# you use Tomas Petricek’s FSharp. Formatting.7

5.旁注, https://github.com/gdeer81/marginalia

5.Marginalia, https://github.com/gdeer81/marginalia

6.Docco http://jashkenas.github.io/docco/

6.Docco, http://jashkenas.github.io/docco/

7.FSharp.Formatting https://github.com/tpetricek/FSharp.Formatting

7.FSharp.Formatting, https://github.com/tpetricek/FSharp.Formatting

传统上,软件程序的文档涉及代码和散文的混合,可以通过多种方式组合:

Traditionally, documentation of a software program has involved a mix of code and prose, which can be combined in several ways:

  • 散文式代码:这是唐纳德·高德纳 (Donald Knuth) 最初提出的文学编程方法。主要文档是遵循程序员人类逻辑的散文。作者程序员可以完全控制叙述。

  • Code in prose: This is the method of literate programming as originally proposed by Donald Knuth. The primary document is prose that follows the human logic of the programmer. The author-programmer has full control of the narration.

  • 代码中的散文:这是大多数编程语言提供的文档生成方法;Javadoc 是用代码创建散文的工具的示例。

  • Prose in code: This is the documentation generation approach offered by most programming languages; Javadoc is an example of a tool that creates prose in code.

  • 将代码和散文分开,通过工具合并到一个文档中:工具用于执行合并以发布文档,例如教程。

  • Separate code and prose, merged into one document by a tool: Tools are used to perform a merge in order to publish a document, such as a tutorial.

  • 代码和散文是同一件事:在这种方法中,编程语言非常清晰,可以作为散文本身来阅读。不幸的是,这个圣杯永远无法实现,但有些编程语言比其他语言更接近这个目标。我看过 Scott Waschlin 编写的一些 F# 代码,它非常接近这个理想状态。

  • Code and prose as the same thing: In this approach, the programming language is so clear that it can be read as prose itself. Unfortunately, this Holy Grail is never reached, but some programming languages get closer than others. I’ve seen some F# code by Scott Waschlin that is impressively close to this ideal.

有些工具(例如 Dexy、8)提供了如何组织代码和散文的选择。

Some tools, like Dexy,8 offer a choice of how to organize the code and the prose with each other.

8.Dexy https://github.com/dexy/dexy

8.Dexy, https://github.com/dexy/dexy

记录你的理由

Recording Your Rationale

在《每个软件架构师应该知道的 97 件事》一书中,Timothy High 说道:“正如‘架构权衡’公理所解释的那样,软件架构的定义就是在各种质量属性、成本、时间之间选择正确的权衡。以及其他因素。” 将“架构”一词替换为“设计”,甚至替换为“代码”,这句话仍然成立。

In the book 97 Things Every Software Architect Should Know, Timothy High is quoted as saying, “As explained in the axiom ‘Architectural Tradeoffs’, the definition of a software architecture is all about choosing the right tradeoffs between various quality attributes, cost, time, and other factors.” Replace the word architecture with design, or even with code, and the sentence still holds.

无论何时做出决定,软件中到处都存在权衡。如果您认为自己没有做出任何权衡,那么这只意味着权衡是看不见的。

There are trade-offs everywhere in software, whenever a decision is being made. If you believe you’re not making any trade-off, it just means the trade-off is out of sight.

决定属于故事。人类喜欢故事并倾向于记住它们。保留决策的背景非常重要。过去决策的背景对于在新的背景下重新评估该决策是必要的。过去的决策是可以帮助学习前人思想的工具。许多决策比其后果更易于描述,因此比决策产生的所有细节更容易从一个大脑转移到另一个大脑。如果您很快告诉我您的意图和背景,只要我是一名熟练的专业人士,我可能会做出与您相同的决定。然而,如果没有意图和背景,你就会想知道“他们在想什么?” (见图4.3)。

Decisions belong to stories. Humans love stories and tend to remember them. It is important to preserve the context of a decision. The context of a past decision is necessary to reevaluate that decisions in a new context. Past decisions are tools that can help learn from the thinking of the predecessors. Many decisions are also more compact to describe than their consequences and hence are easier to transfer from one brain to another than all the details that result from a decision. If you tell me your intent and the context shortly, provided that I’m a skilled professional, I may come up with the same decisions that you’ve made. Without the intent and context, however, you are left to wonder “What were they thinking?” (see Figure 4.3).

图中显示了一个卡通人物想象“他们在想什么?”

图 4.3 他们在想什么?

Figure 4.3 What were they thinking?

因此:以某种形式的持久文档记录每个重要决策的理由。包括上下文和主要替代方案。并聆听文档:如果您发现很难将理由和替代方案正式化,那么该决定可能没有经过深思熟虑。您正在编程可能是巧合!

Therefore: Record the rationale for each important decision in some form of persistent documentation. Include the context and the main alternatives. And listen to the documentation: If you find it hard to formalize the rationale and the alternatives, then it may be that the decision was not as deliberate as it should have been. You may be programming by coincidence!

基本原理是什么?

What’s in a Rationale?

任何决定都是在一定背景下发生的,并且是经过深思熟虑的问题答案之一。因此,基本原理不仅是所选决策背后的原因,而且还包括以下所有内容:

Any decision happens in a context and is one of the considered answers to a problem. Therefore, a rationale is not only the reason behind a chosen decision, it is also all of the following:

  • 当时的背景:背景包括主要的利害关系和关注点,例如当前负载(“只有 1000 个最终用户每周使用该应用程序一次”)或当前优先级(“优先级是尽快探索市场与产品的契合度”)尽可能”)或假设(“预计不会改变”)或人员考虑(“开发团队不想学习 JavaScript”)。

  • The context at the time: The context includes the main stakes and concerns, for example the current load (“Only 1000 end users using the application once a week”) or the current priority (“Priority is exploring the market-product fit as quickly as possible”) or an assumption (“This is not expected to change”) or a people consideration (“The development teams don’t want to learn JavaScript”).

  • 选择背后的问题或要求:问题的示例包括“页面必须在 800 毫秒内加载才能不丢失访问者”和“停用 VB6 模块”。

  • The problem or requirement behind the choice: Examples of problems are “The page must load in less than 800ms to not lose visitors” and “Decommission the VB6 module.”

  • 决定本身而不是所选择的解决方案,并附有一个或多个主要原因:决定和原因的示例是“通用语言仅用英语单词表达,因为它更简单并且每个当前利益相关者更喜欢这种方式”和“这个外观通过漂亮的 API 公开遗留系统,因为没有充分的理由重写遗留系统,但我们仍然希望像全新的一样方便地使用它。”

  • The decision itself instead of the chosen solution, with the main reason or reasons: Examples of decisions and reasons are “The ubiquitous language is expressed with English words only, as it’s simpler and every current stakeholder prefers it that way” and “This facade exposes the legacy system through a pretty API because there is no good reason to rewrite the legacy but we still want to consume it with the same convenience as if it were brand new.”

  • 认真考虑的主要替代方案,也许是为什么没有选择它们,或者如果情况不同,为什么会选择它们:替代方案的示例是“如果需求更加标准,购买现成的解决方案将是更好的选择” ”、“图形结构会更强大,但更难与用户的 Excel 电子表格映射”,以及“如果我们没有对当前的 Oracle 数据库进行所有这些投资,NoSQL 数据存储将是更好的选择。”

  • The main alternatives that were considered seriously and perhaps why they were not selected or why they would be selected if the context were different: Examples of alternative are “Buying an off-the-shelves solution would be a better choice if the needs were more standard,” “A graph structure would be more powerful but is harder to map with the Excel spreadsheets of the users,” and “A NoSQL datastore would be a better choice if we didn’t have all this investment with our current Oracle database.”

正如@CarloPescio 在关于自记录代码的对话中所建议的那样,一般来说,设计原理很大程度上与丢弃的选项有关,因此通常不在代码中

As @CarloPescio suggested in a conversation on self-documenting code, generally speaking, design rationale is very much about discarded options and so is not typically in the code.

明确理由

Making the Rationale Explicit

您可以通过多种方式记录重要决策背后的理由:

You can record the rationale behind important decisions in a number of ways:

  • 特别文档:您需要一份关于需求的明确文档,包括所有质量属性。它需要缓慢地发展,但至少每年一次;仅对于跨越系统大范围的主要属性才需要这样的文档,而不是对于更多的本地决策。第 12 章“实时架构文档”描述了决策日志作为架构问题方法的示例。

  • Ad hoc document: You need an explicit document about the requirements, including all quality attributes. It needs to evolve slowly but still at least once a year; such a document is needed only for the main attributes that span large areas of the system, not for more local decisions. Chapter 12, “Living Architecture Documentation,” describes a decision log as an example of that approach for architectural matters.

  • 注释:记录决策的注释可以包含记录基本原理的字段:@MyAnnotation(rationale ="We only know how to do that way")

  • Annotations: Annotations that document a decision could have fields to record the rationale: @MyAnnotation(rationale ="We only know how to do that way").

  • 博客文章:博客文章比注释甚至临时文档需要更多的时间来编写,良好的写作风格确实很有帮助。然而,你提供了对决策背后的推理和人文背景的人性化描述,即使字里行间提到了政治和个人议程,这使得它很有价值。当对过去的决定产生疑问时,也可以搜索和扫描博客文章。

  • Blog post: A blog post takes more time to write than annotations or even an ad hoc document, and a good writing style is really helpful. However, you provides a human account of the reasoning and the human context behind a decision, even with the politics and personal agenda mentioned between the lines, and this makes it valuable. A blog post may also be searched and scanned when a question arises on a past decision.

超越文档:动机设计

Beyond Documentation: Motivated Design

记录下这个道理,不仅是为了子孙后代或者未来的自己,也是为了以后的自己。当你这样做的时候,它现在也很有用。你需要听什么是困难的信号有些东西可以改进。如果很难找出理由或背景,则可能没有足够认真地考虑该决定,这应该引起警惕。

Recording the rationale is not just for future generations or your future self; it is also useful right now, at the time you do it. You need to listen to what’s difficult as a signal that something could be improved. If it is hard to come up with a rationale, or a context, perhaps the decision was not thought about seriously enough, and this should be an alert.

如果很难为某个决策提出两到三个可靠的替代方案,那么也许会选择第一个合适的解决方案,而无需做任何工作来探索更简单或更好的解决方案。您当前的决定可能不是最佳的,并且可能会造成未来失去机会的后果。当然,一个理由可能是“选择第一个合适的解决方案以尽快进入市场”,但至少这个决定是经过深思熟虑的,相关人员了解后果,并准备好下次重新考虑。

If it is hard to come up with two or three credible alternatives to a decision, then perhaps the first solution that fit was chosen, without any work being done to explore simpler or better solutions. Your current decision might not be optimal, and it can have consequences as lost opportunities in the future. Of course, one rationale may be “First solution that fit was chosen to go to market as quickly as possible,” but at least this decision is deliberate, and those involved understand the consequence and can be ready to reconsider it next time.

如果没有经过深思熟虑的设计决策,并且完全缺乏技能,您将只能拥有一个随机的软件结构。你最终只会得到一堆细节,而处理它的唯一方法就是猜测所涉及的意图。这通常是您在遗留代码中必须处理的问题,我们将在第 14 章“记录遗留应用程序”中详细讨论我的观点是,专注于明确理由有助于做出更好的决策和更好的软件。

In the absence of deliberate design decisions, and with a complete lack of skills, you will just have a random software structure. You just end up with a pile of details, and the only way to deal with it is to guess the intentions involved. This is typically the issues you have to deal with in legacy code, which we discuss in detail in Chapter 14, “Documenting Legacy Applications.” My point is that the focus on making a rationale explicit helps make better decisions and better software.

避免记录猜测

Avoid Documenting Speculation

在《构建微服务》一书中,Sam Newman 建议不要记录推测性需求的解决方案。他描绘了一幅传统架构文档的批判图景,用很多页和很多图表解释了完美的系统将是什么样子,但完全忽略了在实际构建它并使其工作时出现的任何意外的未来障碍。

In the book Building Microservices, Sam Newman advises against documenting solutions to speculative needs. He paints a critical picture of traditional architecture documentation that explains with many pages and many diagrams how the perfect system will be but ignoring completely any unexpected future impediment when it comes to actually build it and make it work.

相反,理由是根据实际需要做出的已被证明是必要的决定。在增量方法中,例如新兴设计,我们逐片扩展解决方案,每个片都由每个时刻最重要的需求驱动。我们经常以准时制的方式工作,正是因为它是投机的解毒剂:我们只在有必要建造它时才建造它。

In contrast, rationales are decisions taken on actual needs that have been proven to be necessary. In incremental approaches, such as emerging design, we grow the solution slice by slice, and each slice is driven by the most important need at each instant. We often work in a just-in-time fashion, precisely because it is an antidote to speculation: We build it just when it becomes necessary to be built.

总的来说,您应该只记录已经构建的内容,以响应实际需求。

Overall, you should document only what has been built, in response to actual needs.

技能作为预先记录的理由

Skills as Pre-Documented Rationales

许多小决策的思考过程已经被解决并记录在文献中。例如,单一职责原则要求将一个做两件事的类分成两个各做一件事的类。没有必要记录每次发生的特定事件,但您可以在一个地方记录一次您一贯遵循的每项原则;我将其称为“承认你的影响”模式,并将在本章稍后进行描述。

The thinking process for many small decisions has already been solved and documented in the literature. For example, the single responsibility principle says to split a class that does two things into two classes that do one thing each. There is no need to document each occurrence of a particular happening, but you may document once, in a single place, each of the principles you consistently follow; I call this the acknowledge your influences pattern and describe it a little later in this chapter.

记录作为变革推动者的基本原理

Recording the Rationale as an Enabler for Change

了解过去决策背后的所有原因可以使您更成功地做出改变,因为您可以故意尊重或拒绝每个决策。可靠地了解这些决定的最佳方法是将它们记录下来;否则推理就会忘记(见图4.4))。如果没有过去每个决定背后的明确理由,您可能想知道更改是否会对您没有想到的问题产生意想不到的影响。如果不了解过去的决定,您可能永远没有足够的把握决定改变,并且现状将占主导地位,即使改进的机会就在您眼前。或者,如果您所做的更改引发了您因未记录而无法看到的被遗忘的问题,那么您实际上可能会无意中造成伤害。

Knowing all the reasons behind past decisions can enable you to more successfully make changes because you can either respect or reject each of those decisions deliberately. The best way to know about those decisions in a reliable way is to have them recorded; otherwise, the reasoning will be forgotten (see Figure 4.4). Without the explicit rationale behind each past decision, you might wonder if a change has unexpected impacts with respect to a concern you don’t have in mind. Without knowing about past decisions, you may never be sure enough to decide to change, and the status quo will dominate, even though the opportunity to improve is there in front of your eyes. Alternatively, you may actually cause harm inadvertently if you make a change that triggers a forgotten concern that you cannot see because it was not recorded.

一个图说明了“不知道为什么,他们会再次犯同样的错误”的概念。

图4.4 不知道为什么,他们会再次犯同样的错误

Figure 4.4 Without the why, they will make the same mistake again

承认你的影响(又名项目书目)

Acknowledging Your Influences (aka Project Bibliography)

好书关心他们的参考书目。对于读者来说,这是了解更多的一种方式,同时也是检验作者影响力的一种方式。当一个单词有不同的含义时,查看参考书目有助于找出如何解释它。读了这本书!

Good books care about their bibliography. For the reader, it’s a way to learn more, but it’s also a way to check the influences of the author. When a word has different meanings, looking at the bibliography helps find out how to interpret it. Read the book!

—Eric Evans,领域驱动设计

—Eric Evans, Domain-Driven Design

项目团队的心态是稳定的知识,值得向未来的开发人员明确说明。不需要长句子;你可以只列出你的参考书目并列出你的风格的要点。

The mindset of the team that worked on a project is stable knowledge that’s worth making explicit for future developers. It doesn’t require long sentences; you can just list your bibliography and list the main points of your style.

项目参考书目为读者提供了上下文。它揭示了团队在构建软件时的影响。项目参考书目由书籍、文章和博客的链接组成,这些链接要么是手工制作的,要么是从注释和评论中提取的,或者是两者结合的。

A project bibliography provides a context for readers. It reveals the influences of the team at the time that they built the software. A project bibliography is composed of links to books, articles, and blogs either crafted by hand or extracted from annotations and comments or through a mix of both.

宣告你的风格

Declaring Your Style

由于画家往往属于特定的绘画流派(例如超现实主义、立体主义),因此软件开发人员会与不同的思想流派保持一致。有些画家可以在一幅作品的风格之间切换到另一幅作品;同样,开发人员也许能够以非常函数式编程风格创建一个模块,其中所有内容都是纯粹且不可变的,然后使用语义技术和面向图形的存储创建另一个模块。

As painters tend to belong to specific painting schools (for example, Surrealism, Cubism), software developers align themselves with various schools of thoughts. Some painters can switch between styles from one work to another; similarly, a developer might be able to create a module in a very functional programming style, with everything pure and immutable, and then create another module using semantic technologies and graph-oriented stores.

为了向文档读者提供上下文,声明您为某些代码区域(通常是模块或项目)选择的样式和主要范例(如果有)非常有用。这个总体陈述可能看起来类似于一个或多个团队的简历:

To provide context for documentation readers, it is useful to declare the style and the main paradigm, if any, that you have chosen for some area of code—typically for a module or for a project. This overall statement might look similar to a resume for the team or teams:

  • 建模范例(例如 DDD)

  • Modeling paradigms (for example, DDD)

  • 团队成员关注的作者

  • Authors the team members follow

  • 团队成员读过的书和经常读的博客

  • Books the team members have read and blogs they often go read

  • 团队成员熟悉的语言和框架

  • Languages and frameworks the team members are familiar with

  • 任何重要的灵感,例如“Stripe 作为开发人员友好性的灵感”

  • Any kind of inspiration that matters, such as “Stripe as an inspiration for developer-friendliness”

  • 团队成员迄今为止完成的典型项目类型(例如,Web、服务器、嵌入式)

  • Typical kinds of projects the team members have mostly done so far (for example, web, server, embedded)

为了作为重构证据,此信息应驻留在模块或项目本身中。它可以通过包 (Java) 上的 (Styles.FP)、AssemblyInfo (.Net) 上的属性等注释来完成@Style,或者通过在模块或项目根部使用具有键/值语法的 style.txt 文件来完成。

To be refactoring proof, this information should reside within the module or project itself. It can be done with annotations such as @Style (Styles.FP) on packages (Java), attributes on the AssemblyInfo (.Net), or by using a style.txt file with a key/value syntax at the root of the module or project.

笔记

Note

显式的样式声明对于工具也很有用;例如,声明的样式可用于为静态分析工具选择特定的规则集。

An explicit style declaration is also useful for tools; for example, the declared style can be used to select a specific rulesets for static analysis tools.

声明您的风格有助于强制代码库内的一致性。

Declaring your style helps enforce consistency within the code base.

图片哈哈

LOL

昨天创造了吉尔克定律:从软件系统的结构中你可以推导出架构师最近读过的书。

Coined Gierke’s law yesterday: from the structure of a software system you can derive the book the architect read most recently.

——奥利弗·吉尔克 (Oliver Gierke),Twitter 上的@olivergierke

—From Oliver Gierke, @olivergierke on Twitter

将消息作为综合文档提交

Commit Messages as Comprehensive Documentation

精心编写的提交消息使每一行代码都有良好的记录。将文件提交到源代码管理时,最好添加包含提交消息的有意义的注释。这一点经常被忽视,结果是浪费时间打开文件来发现更改的内容。如果仔细完成,提交消息对于多种目的非常有价值,作为另一种高收益活动:

Carefully written commit messages make each line of code well documented. When committing files into source control, it is good practice to add a meaningful comment that includes the commit message. This is often neglected, and the result is time wasted opening files to discover what a change was about. When done carefully, commit messages are very valuable for several purposes, as yet another high-yield activity:

  • 思考:你必须思考已完成的工作。应该拆分的是一项单独的更改还是多项更改的组合?清楚吗?真的完成了吗?是否有应该随着更改而添加或修改的新测试?

  • Thinking: You have to think about the work done. Is it one single change or a mix of more than one that should be split? Is it clear? Is it really done? Are there new tests that should have been added or modified along with the changes?

  • 解释:提交消息必须明确意图。它是一个功能,或者一个修复,并且应该写下原因,即使是简短的,就像记录基本原理一样。这将为读者节省时间。

  • Explaining: A commit message must make the intention explicit. It is a feature, or a fix, and the reason should be written, even briefly, as in recording the rationale. This will save time for the readers.

  • 报告:提交消息稍后可用于各种报告、作为变更日志发布或集成到开发人员工具链中。

  • Reporting: Commit messages can later be used for various kinds of reporting, published as a changelog, or integrated into the developer toolchain.

提交消息的重要思想是,在任何给定的代码行上,向源代码管理询问其历史记录可以为您提供详细的原因列表,并希望能够解释为什么这行代码是这样的。正如 Mislav Marohnić 在他的博客文章“每一行代码总是被记录下来”中所写,“项目的历史是其最有价值的文档。” 9

The big idea with commit messages is that on any given line of code, asking the source control for its history gives you a detailed list of reasons and, hopefully, of rationales explaining why this line of code is what it is. As Mislav Marohnić writes in his blog post “Every Line of Code Is Always Documented,” “a project’s history is its most valuable documentation.”9

9. Mislav Marohnić,“每一行代码总是被记录下来”, http://mislav.uniqpath.com/2014/02/hidden-documentation/

9.Mislav Marohnić, “Every Line of Code Is Always Documented,” http://mislav.uniqpath.com/2014/02/hidden-documentation/

查看给定代码行的历史记录可以告诉您谁进行了更改、何时更改以及同时更改了哪些其他文件:例如相关测试。这有助于查明添加的新测试用例,并充当代码测试可追溯性的内置机制。在历史记录中,您还可以找到解释更改以及更改原因的提交消息。

Looking at the history of a given line of code tells you who made a change, when, and what other files were changed at the same time: for example, the related tests. This helps pinpoint to the new test cases that were added and acts as a built-in mechanism for code to test traceability. In the history you would also find the commit message explaining the change and the reasons for the change.

为了充分利用提交消息,如果当前的消息质量不令人满意,那么最好就一套标准的提交准则达成一致。使用标准结构和标准关键字有几个好处。一方面,它更正式,因此也更简洁。使用正式语法,您可以编写:

To make the most of commit messages, it might be a good idea to agree on a standard set of commit guidelines if the current quality of the messages is not satisfactory. Using a standard structure and standard keywords has several benefits. For one thing, it is more formal and, therefore, more concise. With a formal syntax, you can write:

修复(ui):将提交按钮的颜色更改为绿色

fix(ui): change the color of the submit button to green

其书写和阅读都比等效的完整英语句子更短:

which is shorter to write and to read than the equivalent full English sentence:

“这是对 UI 区域的修复,将提交按钮的颜色更改为绿色。”

“This is a fix on the UI area to change the color of the submit button to green.”

结构消息强制要求不会忘记所需的信息,例如提交类型或更改位置。使用形式语法将消息转化为机器可访问的知识,甚至更加美好!

A structure message enforces that the required information, such as the type of commit or the location of the change, will not be forgotten. And using formal syntax turns a messages into machine-accessible knowledge, for even more goodness!

因此:请注意提交消息。就一组提交准则达成一致,并使用半正式语法和标准关键字字典。集体工作或使用同行压力、代码审查或执行工具来确保准则得到遵守。设计指南,以便工具可以使用它们来为您提供更多帮助。

Therefore: Take care with commit messages. Agree on a set of commit guidelines and use a semi-formal syntax and a standard dictionary of keywords. Work collectively or use peer pressure, code reviews, or enforcement tools to ensure that the guidelines are respected. Design the guidelines so that tools can use them to help you more.

提交消息为每行代码提供全面的文档。此信息可通过命令行或源代码管理顶部的图形界面获得,如图4.5所示。

Commit messages provide comprehensive documentation for each line of code. This information is available at the command line or on the graphical interface on top of your source control, as shown in Figure 4.5.

下图显示了 GitHub 上的指责视图,其中显示了著名的 Junit 项目每行的每个贡献。

图 4.5 GitHub 上的 Blame 视图显示了每行的每个贡献,这里是著名的 Junit 项目

Figure 4.5 The blame view on GitHub shows every contribution for each line, here for the famous Junit project

提交指南

Commit Guidelines

提交指南的一个很好的例子是 Angular 提交指南10 ,它指定了提交消息必须如何格式化的严格规则。Angular 网站称,这些规则“会带来更易读的消息,在查看项目历史记录时很容易遵循。而且,我们还使用 git 提交消息来生成 AngularJS 更改日志。” 根据这套特定的指导方针,提交消息的结构必须为标头部分、可选的正文部分和可选的页脚部分,每个部分均由空行分隔,如下所示:

A good example of commit guidelines is the Angular commit guidelines,10 which specify strict rules for how the commit messages must be formatted. These rules, says the Angular website, lead “to more readable messages that are easy to follow when looking through the project history. But also, we use the git commit messages to generate the AngularJS change log.” According to this particular set of guidelines, the commit message must be structured as a header section, an optional body section, and an optional footer section, each separated by a blank line, as shown here:

10.AngularJS https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit

10.AngularJS, https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit

1   <类型> ( <范围> ): <主题>
2
3   <正文>
4
5   <页脚>
1  <type>(<scope>): <subject>
2
3  <body>
4
5  <footer>
指定变更的类型

type必须是以下之一:

type must be one of the following:

  • feat:一个新功能

  • feat: A new feature

  • fix:错误修复

  • fix: A bug fix

  • docs:仅文档更改

  • docs: Documentation-only changes

  • style:不影响代码含义的更改,例如空格、格式、缺少分号等的更改

  • style: Changes that do not affect the meaning of the code, such as changes to whitespace, formatting, missing semicolons, and so on

  • refactor:既不修复错误也不添加功能的代码更改

  • refactor: A code change that neither fixes a bug nor adds a feature

  • perf:提高性能的代码更改

  • perf: A code change that improves performance

  • test:添加缺少的测试的更改

  • test: Changes that add missing tests

  • chore:对构建过程或辅助工具和库的更改,例如文档生成

  • chore: Changes to the build process or auxiliary tools and libraries, such as documentation generation

所有重大变更都必须在页脚中声明,以“重大变更”一词开头,后跟一个空格以及对变更和迁移方面的详细说明。

All breaking changes must be declared in the footer, starting with the words breaking change, followed by a space and a detailed explanation of the change and of the migration aspects.

如果提交与跟踪器中的问题相关,则还应在页脚中引用该问题,并使用跟踪器中问题的标识符。

If a commit is related to issues in a tracker, the issue should be referenced in the footer as well, with the identifier of the issue in the tracker.

以下是与“贸易供给”范围相关的功能示例:

Here is an example of a feature related to the scope “trade feeding”:

1 feat(tradeFeeding):支持负优惠券的交易供给
2 债券
3
4 有些债券的票面利率为负,例如-0.21%。
5 更改验证以不拒绝债券交易
6 负优惠券。
7
8 关闭#8125
1 feat(tradeFeeding): Support trade feeding for negative-coupon
2 bonds
3
4 Some bonds have negative coupon rates, e.g. -0.21 percent.
5 Change the validation to not reject trades on bonds
6 with negative coupons.
7
8 Closes #8125
指定变更范围

前面显示的提交语法是半正式的,包含关键字和自由文本的组合。第一个关键字type表示小列表中的更改类型(功能、修复等)。第二个关键字range表示系统或应用程序中的更改范围,并且特定于上下文。

The commit syntax shown earlier is semiformal, with a combination of keywords and free text. The first keyword, type, denotes the type of change (feature, fix, and so on) out of a small list. The second keyword, scope, denotes the scope of the change in the system or application and is specific to the context.

范围可以涵盖系统的各个方面:

scope can cover various aspects of the system:

  • 环境:示例包括produat、 和dev

  • Environment: Examples include prod, uat, and dev

  • 技术:示例包括RabbitMqSOAPJSONPuppetbuildJMS

  • Technology: Examples include RabbitMq, SOAP, JSON, Puppet, build, and JMS

  • 特征:示例包括pricingauthenticationmonitoringcustomershoppingcartshipping、 和reporting

  • Feature: Examples include pricing, authentication, monitoring, customer, shoppingcart, shipping, and reporting

  • 产品:示例包括booksdvdvodjeweltoy

  • Product: Examples include books, dvd, vod, jewel, and toy

  • 集成:示例包括TwitterFacebook

  • Integration: Examples include Twitter and Facebook

  • 动作:示例包括createamendrevokedispute

  • Action: Examples include create, amend, revoke, and dispute

提交指南可能需要一个主要范围,但您可以添加更多范围,如下所示:

A commit guideline could require a main scope, but you could add more, as shown here:

1 项壮举(定价、点播):提高黄金时段的费率
2 ...
1 feat(pricing, vod): increase the rate on prime time
2 ...

当然,您必须定义一个范围列表,最好是整个团队,包括三个朋友,每个人都参与 DevOps 密切合作。可以提交到源代码控制的每一项更改都应至少包含在其中一个范围内。

Of course, you have to define a list of scopes, ideally as a whole team and including the three amigos, with everyone involved in the DevOps in close collaboration. Every change that could be committed to the source control should be covered in at least one of the scopes.

请记住,智能的范围列表为推理影响打开了大门。

Keep in mind that a smart list of scopes opens the door to reasoning about impacts.

机器可访问的信息

提交消息的半正式语法的优点是使机器可以使用这些消息来自动执行更多杂务,例如生成更改日志文档。11让我们仔细看看 Angular.js,它在这方面提供了一个简洁的示例。

A semiformal syntax for commit messages has the benefit of making it possible for machines to use these messages to automate more chores, such as generating a change log document.11 Let’s take a closer look at Angular.js, which provides a neat example in this area.

11. “保留变更日志”, http://keepachangelog.com

11.“Keep a Changelog,” http://keepachangelog.com

根据 Angular.js 约定,每个版本的更改日志由三个可选部分组成,并且每个部分仅在不为空时才显示:

Under Angular.js conventions, the change log is made of three optional sections for each version, and each section is shown only when it is not empty:

  • 新功能

  • New features

  • Bug修复

  • Bug fixes

  • 重大变化

  • Breaking changes

以下是 Angular.js 变更日志的摘录:

The following is an excerpt from an Angular.js change log:

## 0.13.5 (2015-08-04)
### Bug修复
- 文件列表:确保 autowatchDelay 正常工作。(655599a), 关闭
第1520章
- 文件列表:使用 lodash find() (3bd15a7),关闭 #1533
### 特征
- 网络服务器:允许在 https 上运行 (1696c78)
## 0.13.5 (2015-08-04)
### Bug Fixes
- file-list: Ensure autowatchDelay is working. (655599a), closes
#1520
- file-list: use lodash find() (3bd15a7), closes #1533
### Features
- web-server: Allow running on https (1696c78)

此更改日志采用 Markdown 格式,可提供用于在提交、版本和票务系统之间方便导航的链接。例如,每个变更日志中的版本链接到 GitHub 中相应的比较视图,显示此版本与上一版本之间的差异。每个提交消息还链接到其特定的提交,并且还链接到相应的问题(如果适用)。

This change log is in the Markdown format, which enables links for convenient navigation between commits, versions, and ticketing systems. For example, each version in the change log links to the corresponding compare view in GitHub, showing the differences between this version and the previous one. Each commit message also links to their particular commits and also links to the corresponding issue(s), when applicable.

得益于这种结构化提交指南,可以通过命令行魔术来提取和过滤提交,如以下示例所示(借自 Angular.js 文档):

Thanks to this kind of structured commit guidelines, it is possible to extract and filter commits through command-line magic, as shown in the following example, borrowed from the Angular.js documentation:

1 自此以来所有主题的列表(提交消息中的第一行)
最后发布:
2 >> git log <最后一个标签> HEAD --pretty=格式:%s
3
4 此版本的新功能
5 >> git log <最新版本> HEAD --grep 功能
1 List of all subjects (first lines in commit message) since
last release:
2 >> git log <last tag> HEAD --pretty=format:%s
3
4 New features in this release
5 >> git log <last release> HEAD --grep feature

此处显示的更改日志可以在发布时由脚本生成。有许多开源项目可以做到这一点,例如 convention-changelog 项目。12此更改日志自动化脚本强烈依赖于所选的提交指南,并且它已经支持其中的几个指南,包括 Atom、Angular 和 jQuery。

The change log shown here can be generated by a script when doing a release. There are many open-source projects to do this, such as the conventional-changelog project.12 This change log automation script relies strongly on the chosen commit guidelines, and it already supports several of them, including Atom, Angular, and jQuery.

12. https://github.com/ajoslin/conventional-changelog

12.https://github.com/ajoslin/conventional-changelog

这种自动化很方便,尽管人们应该在向公众发布之前检查和编辑生成的更改日志框架。

Such automation is convenient, although if a human should review and edit the generated change log skeleton before release to the public.

概括

Summary

通常情况下,系统中缺失的知识元素正是您想要记住的东西。特别是,您应该记录决策背后的理由。您需要扩充系统的代码以使其知识完整。在这种增强代码方法中,注释、约定和其他技术对于记录最重要的知识片段非常有用。这种增强代码的过程也是一个向同事传播技能的机会,作为一种嵌入式学习的形式。

It is often the case that elements of knowledge that are missing from a system are things that you want to be remembered. In particular, you should record the rationale behind decisions. You need to augment the code of a system to make it knowledge complete. Annotations, conventions, and other techniques are instrumental in this augmented code approach for recording the most important pieces of knowledge. And this process of augmented code is also an opportunity to spread skills to your workmates as a form of embedded learning.

第5章

Chapter 5

生活策展:识别权威知识

Living Curation: Identifying Authoritative Knowledge

女王的演讲就像英国小新版本的发行说明!

The Queen’s speech is like the release notes for a minor new version of the UK!

——推特上的马特·拉塞尔 (@MattRussellUK)

—Matt Russell (@MattRussellUK) on Twitter

请记住,与系统相关的大部分知识已经存在于该系统中,而且数量很多。利用所有这些知识的一个重要方法是通过管理。策展的想法是从系统的数据海洋中选择少数相关的知识,以帮助人们在未来的工作任务中处理这些知识。由于该系统总是在变化,因此最安全的做法是确保该管理自然发展,无需任何手动维护。

Remember that most of the knowledge related to a system is already there in that system—and there is a lot of it. One crucial way to exploit all that knowledge is through curation. The idea of curation is to select the few relevant bits of knowledge out of the ocean of data in the system, in order to help people working on it in their future work assignments. Because this system is always changing, it is safest to ensure that this curation evolves naturally, without any manual maintenance.

动态策展

Dynamic Curation

在艺术展览中,策展人就像电影中的导演一样重要。在当代艺术中,策展人常常选择并解读艺术作品。例如,策展人搜索以前的作品和激发艺术家灵感的地方,然后他或她提出一种叙事或结构化分析,以超越每件单独作品的方式将所选作品联系在一起。当展览所必需的作品不在馆藏中时,策展人会从其他博物馆或私人收藏中借用,甚至可能委托艺术家创作。除了选择作品外,策展人还负责撰写标签和目录文章,并监督展览的布景,以帮助传达所选的信息。

In art exhibitions, the curator is as important as the director in a movie. In contemporary art, the curator selects and often interprets works of art. For example, the curator searches for prior work and places that inspired the artist, and he or she proposes a narrative or a structured analysis that links the selected works together in a way that transcends each individual piece. When a work that is essential for the exhibition is not in the collection, the curator will borrow it from another museum or from a private collection or may even commission the artist to create it. In addition to selecting works, the curator is responsible for writing labels and catalog essays and overseeing the scenography of the exhibition to help convey the chosen messages.

当谈到文档时,我们需要成为自己的策展人,研究所有已有的知识,将其转化为有意义和有用的东西。

When it comes to documentation, we need to become our own curators, working on all the knowledge that is already there to turn it into something meaningful and useful.

策展人根据许多客观标准来选择艺术品,例如艺术家姓名、作品创作的日期和地点,或者首先购买作品的私人收藏家。它们还依赖于更主观的标准,例如与艺术运动或历史上重大事件(例如战争或流行丑闻)的关系。策展人需要有关每幅画、雕塑或视频表演的元数据。当元数据丢失时,策展人必须创建它,有时通过进行研究。

Curators select works of art based on many objective criteria, such as the artist name, the date and place of creation of the works, or the private collectors who first bought the works. They also rely on more subjective criteria, such as the relationships to art movements or to major events in history, such as wars or popular scandals. The curator needs metadata about each painting, sculpture, or video performance. When the metadata is missing, the curator has to create it, sometimes by doing research.

策展是你已经在做的事情,也许你没有意识到。例如,当您被要求向客户或高层管理人员演示应用程序时,您必须选择几个用例和屏幕来展示,以传达信息,例如“一切都在控制之中”或“购买我们的产品,因为它将帮助您完成工作。” 如果您没有传达任何潜在信息,那么您的演示很可能会变得一团糟,无法令人信服。

Curation is something that you already do, perhaps without being aware of it. For example, when you are asked to demo an application to a customer or to a top manager, you have to choose just a few use cases and screens to show in order to convey a message, such as “everything is under control” or “buy our product because it will help you do your job.” If you have no underlying message, it’s likely that your demo with be an unconvincing mess.

与艺术展览不同,软件开发中我们需要的更像是一个活生生的展览,内容会根据最新的变化进行调整。随着知识随着时间的推移而发展,我们需要对最重要的主题进行自动化管理。

Unlike in art exhibitions, in software development what we need is more like a living exhibition with content that adjusts according to the latest changes. As the knowledge evolves over time, we need to automate the curation on the most important topics.

因此:采用策展人的心态,利用源代码和工件中的所有可用知识讲述一个有意义的故事。不要选择固定的元素列表。相反,依靠每个工件中的标签和其他元数据来动态选择长期感兴趣的内聚知识子集。当缺少必要的元数据时增强代码,并在故事需要时添加任何缺少的知识片段。

Therefore: Adopt the mindset of a curator to tell a meaningful story out of all the available knowledge in the source code and artifacts. Don’t select a fixed list of elements. Instead, rely on tags and other metadata in each artifact to dynamically select the cohesive subset of knowledge that is of interest for the long term. Augment the code when the necessary metadata is missing and add any missing pieces of knowledge when they are needed for the story.

策展是从大量收藏中选择相关作品以创建一致的叙述来讲述故事的行为。这就像混音或混搭。策展是软件开发等知识工作的关键。源代码充满了开发的许多方面的知识,并且它的不同部分具有不同程度的重要性。在任何比玩具应用程序更大的东西上,从源工件中提取知识会立即因为太多细节而溢出我们的常规认知能力,并且这些知识变得毫无意义,因此毫无用处(见图 5.1

Curation is the act of selecting relevant pieces out of a large collection to create a consistent narrative that tells a story. It’s like a remix or a mashup. Curation is key for knowledge work like software development. Source code is full of knowledge about many facets of the development, and different parts of it are of varying degrees of importance. On anything bigger than a toy application, extracting knowledge from the source artifacts immediately overflows our regular cognitive capabilities with too many details, and the knowledge becomes meaningless and therefore useless (see Figure 5.1).

图为卡通人物,说明“信息太多和没有信息一样无用”。

图5.1 太多的信息和没有信息一样无用

Figure 5.1 Too much information is as useless as no information

解决方案是针对特定的通信意图积极滤除噪音中的信号;正如图 5.1中的小小丑怪物所说,“太多的信息和没有信息一样无用。” 从某个特定角度来看,噪音可能是从另一个角度来看的信号。例如,方法名称在体系结构图中是不必要的细节,但在关于两个类如何交互(其中一个类是另一个类的适配器)的特写图中,它们可能很重要。

The solution is to aggressively filter the signal from the noise for a particular communication intent; as the little buffoon monster in Figure 5.1 says, “Too much information is as useless as no information.” What would be the noise from a particular perspective might be the signal from another perspective. For example, the method names are an unnecessary detail in an architecture diagram, but they might be important in a close-up diagram about how two classes interact, with one being an adapter to the other.

策展的核心是根据选定的编辑视角选择要包含或忽略的知识片段。这是一个范围问题。动态管理更进一步,能够对一组不断变化的工件进行连续选择。

Curation as its core is the selection of pieces of knowledge to include or to ignore, according to a chosen editorial perspective. It’s a matter of scope. Dynamic curation goes one step further, with the ability to do the selection continuously on an ever-changing set of artifacts.

动态策展的例子

Examples of Dynamic Curation

Twitter 搜索是自动动态管理的一个示例,它本身就是一种资源,您可以像关注任何 Twitter 句柄一样关注它。Twitter 上的人们在转发他们(或多或少)根据自己的编辑观点(也许)精心挑选的内容时,也会进行手动形式的策展。谷歌搜索是简单自动管理的另一个例子。

A Twitter search is an example of automated dynamic curation, and it is a resource in itself that you can follow just as you would follow any Twitter handle. People on Twitter also do a manual form of curation when they retweet content they have (more or less) carefully selected according to their own editorial perspective (perhaps). A Google search is another example of simple automated curation.

再举一个例子,我们在使用 IDE 时每天都会做的事情是根据标准选择最新的工件子集:

As another example, selecting an up-to-date subset of artifacts based on a criterion is something we do every day when using an IDE:

  • 显示名称以“DAO”结尾的每种类型。

  • Show every type with a name that ends with “DAO.”

  • 显示调用此方法的每个方法。

  • Show every method that calls this method.

  • 显示引用该类的每个类。

  • Show every class that references this class.

  • 显示引用此注释的每个类。

  • Show every class that references this annotation.

  • 显示属于该接口子类型的每个类型。

  • Show every type that is a subtype of this interface.

当缺少帮助选择片段的标签时,您应该通过注释、命名约定或任何其他方式来引入它。当某条知识缺失时,为了展现完整的画面,你需要及时添加它。

When a tag is missing to help select the pieces, you should introduce it with annotations, naming conventions, or any other means. When a piece of knowledge is missing, in order to show a complete picture, you need to add it in a just-in-time fashion.

编辑策划

Editorial Curation

策展是一种编辑行为。决定编辑观点是至关重要的一步。一次应该只有一条消息。一个好的消息是带有动词的陈述,例如“不允许从领域模型层到其他层的依赖关系”,而不仅仅是“层之间的依赖关系”,其中不存在任何依赖关系。消息,由读者来猜测其含义。至少,动态管理应该被赋予一个能够反映预期信息的富有表现力的名称。

Curation is an editorial act. Deciding on an editorial perspective is the essential step. There should be one and only one message at a time. A good message is a statement with a verb, like “No dependency is allowed from the domain model layer to the other layers” rather than just “Dependencies between layer,” where there is no message, and it’s up to the reader to guess what is meant. At a minimum, dynamic curation should be given an expressive name that reflects the intended message.

低维护动态管理

Low-Maintenance Dynamic Curation

如果以严格的方式选择知识子集可能会很危险。例如,对类、测试或场景列表的直接引用将很快变得过时并且需要维护。它是一种复制和粘贴的形式,使得更改成本更高;它还存在有人忘记更新的风险。这不是一个好的做法,应该不惜一切代价避免。

Selecting subsets of knowledge can be hazardous if done in a rigid way. For example, a direct reference to a list of classes, tests, or scenarios will rapidly become obsolete and will require maintenance. It is a form of copy and paste, and it makes change more expensive; it also is subject to the risk of someone forgetting to update it. This is not a good practice, and it should be avoided at all cost.

警告

Caution

避免通过名称或 URL 直接引用工件。相反,找到一种机制来根据随着时间推移保持稳定的标准来选择知识片段,以便选择的内容能够保持最新状态,而无需任何手动操作。

Avoid directly referencing artifacts by name or URL. Instead, find mechanisms to select pieces of knowledge based on criteria that are stable over time so that the selection will remain up-to-date without any manual action.

关键概念

Key Concept

基于稳定的标准间接选择工件。

Select artifacts indirectly, based on stable criteria.

您可以使用此处描述的稳定选择标准之一以稳定的方式描述感兴趣的工件:

You can describe the artifacts of interest in a stable way by using one of the stable selection criteria described here:

  • 文件夹组织:例如,“名为‘退货政策’的文件夹中的所有内容”

  • Folder organization: For example, “everything in the folder named ‘Return Policy’”

  • 命名约定:例如,“名称中包含“Nominal”的每个测试”

  • Naming conventions: For example, “every test with ‘Nominal’ in its name”

  • 标签或注释:例如,“标记为‘WorkInProgress’的每个场景”

  • Tags or annotations: For example, “every scenario tagged as ‘WorkInProgress’”

  • 您可以控制的链接注册表(可能需要不时进行一些维护,但至少它位于中心位置):例如,“在此短链接下注册的 URL”

  • Links registry over which you have control (which may need some maintenance from time to time, but at least it is in a central place): For example, “the URL registered under this shortlink”

  • 工具输出:例如,“编译器已处理的每个文件,如其日志中所示”

  • Tool output: For example, “every file that has been processed by the compiler, as visible in its log”

当您使用稳定的标准时,工作是通过自动提取符合条件的最新内容并将其插入到已发布的输出中的工具来完成的。因为它是完全自动化的,所以它可以尽可能频繁地运行——也许在每个构建上连续运行。

When you use stable criteria, the work is done by tools that automatically extract the latest content that meets the criteria to insert it into the published output. Because it is fully automated, it can be run as often as possible—perhaps continuously on each build.

一套知识集多种用途

One Corpus of Knowledge for Multiple Uses

一切都可以策划——代码、配置、测试、业务行为场景、数据集、工具、数据等等。所有可用的知识都可以被视为一个巨大的语料库,可以通过自动化手段进行分析和策划提取。

Everything can be curated—code, configuration, tests, business behavior scenarios, datasets, tools, data, and so on. All the knowledge available can be considered as a huge corpus, accessible via automated means for analysis and curated extractions.

如果知识库的内容被充分标记,则可以通过管理从其中提取术语表的业务视图(即,活的术语表)、架构的技术视图(即,活的图表) ),以及您可以想象的任何其他观点,包括以下内容:

Provided that the content of the knowledge corpus is adequately tagged, it is possible to extract by curation out of it a business view of a glossary (that is, a living glossary), a technical view of the architecture (that is, a living diagram), and any other perspective you can imagine, including the following:

  • 特定于受众的内容,例如仅限业务可读的内容与技术细节

  • Audience-specific content, such as business-readable content only versus technical details

  • 特定于任务的内容,例如如何添加另一种货币

  • Tasks-specific content, such as how to add one more currency

  • 针对特定目的的内容,例如内容概述与参考部分

  • Purpose-specific content, such as an overview of content versus a references section

仅当有关源知识的元数据可用以实现感兴趣材料的相关选择时,策展才是可能的。

Curation is possible only to the extent that metadata about the source knowledge is available to enable relevant selection of material of interest.

情景摘要

Scenario Digests

策展不仅仅涉及代码;还涉及代码。它还与测试和场景有关。动态管理的一个很好的例子是场景摘要,其中业务场景的语料库在不同的维度下进行管理,以便发布针对特定受众和目的定制的报告。

Curation is not just about code; it’s also about tests and scenarios. A good example of dynamic curation is a scenario digest, in which the corpus of business scenarios is curated under various dimensions in order to publish reports tailored for particular audiences and purposes.

当团队将 BDD 与 Cucumber 等自动化工具一起使用时,大量场景会写入功能文件中。并非每个场景对于每个人和每个目的都同样有趣,因此您需要一种方法来对场景进行动态管理,为此您需要使用精心设计的标签系统来标记场景。请记住,在第 2 章“行为驱动开发作为生活规范的示例”中,标签就是文档。

When a team makes use of BDD together with an automated tool such as Cucumber, a large number of scenarios are written in feature files. Not every scenario is equally interesting for everyone and for every purpose, so you need a way to do a dynamic curation of the scenarios, and for that you need to have the scenarios marked with a nicely designed system of tags. Remember from Chapter 2, “Behavior-Driven Development as an Example of Living Specifications,” that tags are documentation.

每个场景都可以有如下标签:

Each scenario can have tags like the following:

1 @acceptancecriteria @specs @returnpolicy @nominalcase
@keyexample
2 场景:30天内退货全额退款
3 ...
4
5 @acceptancecriteria @specs @returnpolicy @nominalcase
6 场景:超过30天退货不予报销
7  ...
8
9 @specs @returnpolicy @controversial
10 场景:无购买凭证退货不予报销
11 ...
12
13 @specs @returnpolicy @wip @negativecase
14 场景:未知返回错误
15 ...
1  @acceptancecriteria @specs @returnpolicy @nominalcase
@keyexample
2  Scenario: Full reimbursement for return within 30 days
3  ...
4
5  @acceptancecriteria @specs @returnpolicy @nominalcase
6  Scenario: No reimbursement for return beyond 30 days
7  ...
8
9  @specs @returnpolicy @controversial
10 Scenario: No reimbursement for return with no proof of purchase
11  ...
12
13 @specs @returnpolicy @wip @negativecase
14 Scenario: Error for unknown return
15  ...

请注意,几乎所有这些标签都是完全稳定的,并且对于它们相关的场景来说是固有的。我说几乎是因为@controversial@wip(正在进行的工作)实际上并不意味着持续太久,但它们可以方便地持续几天或几周,以便于报告。

Note that almost all these tags are totally stable and intrinsic to the scenario they relate to. I say almost because @controversial and @wip (work in progress) are actually not meant to last too long, but they are convenient for a few days or weeks for easy reporting.

借助所有这些标签,可以轻松地仅通过标题或完整的分步描述来提取场景的子集。以下是一些示例:

Thanks to all these tags, it is easy to extract only a subset of scenarios, by title only or complete with step-by-step descriptions. The following are some examples:

  • @keyexample当与时间非常有限的业务专家会面时,也许您可​​以只关注标记为和 的信息@controversial

    1 @keyexample 或 @controversial 场景:
    2 - 30 天内退货全额退款
    3 - 无购买证明的退货不予报销
  • When meeting business experts who have very limited time, perhaps you could focus only on the information tagged @keyexample and @controversial:

    1 @keyexample or @controversial Scenarios:
    2 - Full reimbursement for return within 30 days
    3 - No reimbursement for return with no proof of purchase
  • 当向赞助商报告进展情况时,观众可能更感兴趣的是@wip和场景,以及通过绿灯@pending的比例:@acceptancecriteria

    1 @wip、@pending 或 @controversial 场景:
    2 - 未知返回错误
  • When reporting to the sponsor about the progress, the @wip and @pending scenarios are probably more interesting for this audience, along with the proportion of @acceptancecriteria passing green:

    1 @wip, @pending or @controversial Scenarios:
    2 - Error for unknown return
  • 当新团队成员入职时,浏览@nominalcase每个@specs部分的场景可能就足够了:

    1 @nominalcase 场景:
    2 - 30 天内退货全额退款
    3 - 超过 30 天退货不予报销
  • When onboarding a new team member, going through the @nominalcase scenarios of each @specs section may be enough:

    1 @nominalcase Scenarios:
    2 - Full reimbursement for return within 30 days
    3 - No reimbursement for return beyond 30 days
  • 合规官员想要一切不想要的东西@wip@acceptancecriteria然而,即使在这种情况下,他们也可能希望让大文档在附录中显示第一个场景和其余场景的摘要。

  • Compliance officers want everything that is not @wip. However, even in that case, they might want to have the big document show a summary of the @acceptancecriteria first and the rest of the scenarios in addendum.

突出核心

Highlighting the Core

域中的某些元素比其他元素更重要。在《领域驱动设计》一书中,Eric Evans 解释说,当一个领域增长到大量元素时,它就会变得难以理解,即使只有一小部分元素确实重要。引导开发人员关注特定子集的一个简单方法是在代码存储库本身中突出显示它们。他将该子集称为突出显示的核心

Some elements of a domain are more important than others. In the book Domain-Driven Design, Eric Evans explains that when a domain grows to a large number of elements, it becomes difficult to understand, even if only a small subset of the elements are really important. A simple way to guide developers to focus on particular subsets is to highlight them in the code repository itself. He calls that subset the highlighted core.

因此:在模型的主存储库中标记核心域的每个元素,而无需特别尝试阐明其角色。让开发人员轻松了解核心内部或外​​部的内容。

Therefore: Flag each element of the core domain within the primary repository of the model without particularly trying to elucidate its role. Make it effortless for a developer to know what is in or out of the core.

使用注释直接在代码中标记核心概念是一种自然的方法,并且随着时间的推移它会不断发展。类或接口等代码元素会被重命名,从一个模块移动到另一个模块,有时最终会被删除。

Using annotations to flag the core concepts directly in the code is a natural approach, and it evolves well over time. Code elements such as classes or interfaces get renamed, are moved from one module to another, and sometimes end up deleted.

以下是一个通过注释进行管理的完美简单示例:

The following is a perfect simple example of curation by annotations:

1   /** 
2    * 加油卡及其类型、id、持有者姓名
3    */
4 @ValueObject
5 @CoreConcept
6 公共类 FueldCard {
7 私有最终字符串id;
8 私有最终字符串名称;
9 ...
1  /**
2   * A fuel card with its type, id, holder name
3   */
4  @ValueObject
5  @CoreConcept
6  public class FueldCard {
7   private final String id;
8   private final String name;
9   ...

它是集成到 IDE 搜索功能中的内部文档。只需搜索项目中注释的每个引用,您就可以看到所有核心概念的列表,并且始终是最新的(见图5.2)。

It is an internal documentation integrated into the search capabilities of an IDE. You can see the list of all core concepts just by searching every reference of the annotation in the project, which is always up-to-date (see Figure 5.2).

该图显示了选择搜索选项卡的部分屏幕截图,其中显示了许多参考文献,其中突出显示了“加油卡监控”。 通过搜索对 @CoreConcept 注释的所有引用,可以随时在 IDE 中立即使用突出显示的核心。

图 5.2通过搜索对 注释的所有引用,可以随时在 IDE 中立即使用突出显示的核心@CoreConcept

Figure 5.2 The highlighted core is available instantly and at any time in the IDE through a search on all references to the @CoreConcept annotation

当然,工具还可以扫描源代码并使用突出显示的核心作为改进管理的便捷且相关的方式。例如,生成图表的工具可能会在不同情况下显示不同级别的细节,例如当元素少于七个时显示所有内容,而当元素多于七个时仅关注突出显示的核心。动态术语表通常使用这种技术来突出显示术语表中最重要的元素,方法是首先显示它们或以粗体字体打印它们。

And, of course, tools can also scan the source and use the highlighted core as a convenient and relevant way to improve curation. For example, a tool to generate a diagram may show different levels of detail in different cases, such as showing everything when there are fewer than seven elements and focusing only on the highlighted core when there are many more than seven elements. A living glossary typically uses this technique to highlight the most important elements in the glossary by showing them first or by printing them in a bold font.

突出鼓舞人心的榜样

Highlighting Inspiring Exemplars

关于如何编写代码的最佳文档通常是已经存在的代码。当我在 TDD 上指导团队时,我会与开发人员在我以前从未见过的代码库上随机配对编程。与我合作的开发人员常常表现得好像他们以前从未见过代码库一样;对于新任务,他们可能会寻找已有的类似示例,然后将其复制并粘贴到新案例中。例如,程序员可能会决定查找由 Fred 编写的服务,Fred 是团队负责人,并且受到团队其他成员的尊重。然而,Fred 可能并不在其代码的每个方面都表现出色,并且其代码中的缺陷最终可能会在整个代码库中复制。在这种情况下,提高代码质量的一个好方法是改进人们模仿的代码示例。示例代码应该作为模仿的理想模型,或者至少激励其他开发人员。萨姆·纽曼在他的书中写到了这一点构建微服务

The best documentation on how to write code is often the code that is already there. When I’m coaching teams on TDD, I pair-program randomly with developers on code bases I have never seen before. The developers pairing with me often behave as if they have never seen the code base before; for a new task, they might go looking for an example of something similar already there, and then they copy and paste it into a new case. A programmer might determine, for example, to find a service written by Fred, who is the team lead and is well respected by the rest of the team. However, Fred might not be great in every aspect of his code, and the flaws in his code may end up being replicated across the whole code base. In such situations, a good way to improve the code quality is to improve the examples of code that people imitate. Exemplary code should serve as a desirable model to imitate—or at least to inspire other developers. Sam Newman writes about this in his book Building Microservices:

如果您有一套想要鼓励的标准或最佳实践,那么拥有可以向人们指出的范例会很有用。这个想法是,人们不会仅仅通过模仿系统中一些更好的部分而犯下太大的错误。1

If you have a set of standards or best practices you would like to encourage, then having exemplars that you can point people to is useful. The idea is that people can’t go far wrong just by imitating some of the better parts of your system.1

1.纽曼,萨姆。构建微服务。加利福尼亚州塞瓦斯托波尔:O'Reilly Media, Inc.,2015。

1.Newman, Sam. Building Microservices. Sebastopol, CA: O’Reilly Media, Inc., 2015.

您可以在对话期间以及结对编程或群体编程期间向您的同事指出示例:“让我们看一下类ShoppingCartResource,它是设计最精良的类,并且完全符合我们作为团队所喜欢的代码风格。”

You can point your colleagues to the exemplars during conversations and during pair-programming or mob-programming: “Let’s look at the class ShoppingCartResource, which is the most well-designed class and is exactly in the style of code we favor as a team.”

对话对于共享示例来说是完美的,但是当您不在场为人们指明正确的方向或当人们独自工作时,一些额外的文档也可以带来好处。您可以使用文档提供相当于一个响亮的大标志来表示良好的示例(参见图 5.3)。

Conversations are perfect for sharing examplars, but some additional documentation can have benefits, too, when you are not present to point people in the right direction or when people are working on their own. You can use documentation to provide the equivalent of a big loud sign to signal a good examples (see Figure 5.3).

图中显示了三个编码,其中中间的一个被标记为“这里是好例子”。

图 5.3 这里是很好的代码示例!

Figure 5.3 Good example of code here!

因此:直接在实际生产代码中突出显示您想要鼓励的风格或最佳实践的特别好的范例。向您的同事介绍这些范例并宣传如何找到他们自己。照顾好范例,使它们保持模范性,让每个人都可以模仿,从而改进整体代码库。

Therefore: Highlight directly in the actual production code the places that are particularly good exemplars of a style or of a best practice you would like to encourage. Point your colleagues to these exemplars and advertise how to find them on their own. Take care of the exemplars so that they remain exemplary, for everyone to imitate in a way that will improve the overall code base.

当然,注释非常适合这里:您可以创建自定义注释来添加最具代表性的几个类或方法。当然,只有当样本数量仅限于最好的样本时,样本才有用。

Annotations are, of course, a perfect fit here: You can create a custom annotation to put on the few classes or methods that are the most exemplary. Of course, exemplars are useful only if their numbers are limited to those that are very best.

关于哪些代码是模范的或不是模范的,最好由团队集体决定。使其成为一项团队练习,以在少数示例上达成共识,并用特殊注释进行突出显示。

Decisions on what code is exemplary or not are best made collectively by the team. Make it a team exercise to find a consensus on the few exemplars to highlight with a special annotation.

示例应该是生产中使用的实际代码,而不是教程代码,正如 Sam Newman 在《构建微服务》中所说:

Exemplars should be actual code used in production, not tutorial code, as Sam Newman says in Building Microservices:

理想情况下,这些应该是您拥有的能够使事情正确的现实世界服务,而不是仅仅作为完美示例而实现的孤立服务。通过确保您的范例得到实际使用,您就可以确保您拥有的所有原则实际上都有意义。2

Ideally, these should be real world services you have that get things right, rather than isolated services that are just implemented to be perfect examples. By ensuring your exemplars are actually being used, you ensure that all the principles you have actually make sense.2

2.纽曼,萨姆。构建微服务。加利福尼亚州塞瓦斯托波尔:O'Reilly Media, Inc.,2015。

2.Newman, Sam. Building Microservices. Sebastopol, CA: O’Reilly Media, Inc., 2015.

在实践中,一个范例很难在所有方面都是完美的。它可能是一个非常好的设计示例,但代码风格可能有点弱——或者反之亦然。我首选的解决方案是首先修复薄弱环节。但是,如果这是不可能或不可取的,您至少应该澄清为什么该示例是好的,以及它的哪些方面不应被视为示例。以下是一些范例:

In practice, an exemplar is hardly perfect in all aspects. It might be a very good example of design, but the code style might be a bit weak—or the other way round. My preferred solution would be to fix the weak aspect first. However, if that’s not possible or desirable, you should at least clarify why the exemplar is good and what aspect of it should not be considered exemplary. Here are a few examples of exemplars:

  • 上一堂课: @Exemplar("A very good example of a REST resource with content negotiation and the use of URI-templates")

  • On a class: @Exemplar("A very good example of a REST resource with content negotiation and the use of URI-templates")

  • 在 JavaScript 文件上: @Exemplar("The best example of integrating Angular and Web Components")

  • On a JavaScript file: @Exemplar("The best example of integrating Angular and Web Components")

  • 在这部分设计的包或关键类上: @Exemplar("A nicely designed example of CQRS")

  • On a package or a key class of this part of design: @Exemplar("A nicely designed example of CQRS")

  • 在特定的班级上: @Exemplar(pros = "Excellent naming of the code", cons = "too much mutable state, we recommend immutable state")

  • On a particular class: @Exemplar(pros = "Excellent naming of the code", cons = "too much mutable state, we recommend immutable state")

基本上,直接在代码中标记示例使您能够向 IDE 询问诸如“什么代码是编写 REST 资源的一个很好的示例?”之类的问题。在集成文档方式中,查找示例只需@Exemplar在 IDE 中搜索注释的所有引用即可。然后,您只需滚动简短的结果列表即可决定哪些代码将成为您任务的灵感。

Basically, marking exemplars directly in the code enables you to then ask your IDE something like “What code is a good example of writing a REST resource?” In an integrated documentation fashion, finding exemplars is only a matter of searching for all references of the @Exemplar annotation in your IDE. You can then just scroll the short list of results to decide which code will be the inspiration for your task.

当然,之前建议的方法有一些注意事项:

Of course, there are caveats in the approach suggested before:

  • 软件开发不应该像思考和解决问题那样进行复制和粘贴。突出显示示例并不授予您复制和粘贴代码的许可。

  • Software development is not supposed to be as much copying and pasting as thinking and solving problems. Highlighting exemplars does not give you license to copy and paste code.

  • 复制/粘贴需要重构。随着类似代码的积累,必须对其进行重构。

  • Copying/pasting requires refactoring. As similar code accumulates, it must be refactored.

  • 在代码中标记示例并不意味着取代向同事询问示例代码。提出问题是件好事,因为它会引发对话,而对话是改进代码和技能的关键。当被要求提供范例时,不要回答“RTFM”(“阅读翻转手册”)。相反,请一起浏览 IDE 中建议的示例,以确定哪一个最适合该任务。始终将对话视为相互改进的机会。

  • Marking the exemplars in the code is not meant to replace asking colleagues for exemplary code. Asking questions is good because it leads to conversations, and conversations are key for improving the code and the skills. Don’t reply “RTFM” (“read the flipping manual”) when asked for exemplars. Instead, go through the suggested exemplars in the IDE together to determine which one would be best for the task. Always take conversations as opportunities to improve something mutually.

导游和观光地图

Guided Tours and Sightseeing Maps

通过导游或观光地图可以更轻松地快速发现新地方的最佳景点。在一个你从未去过的城市里,你可以随机探索,希望能碰到一些有趣的东西。这是我在较长时间住宿期间喜欢在下午做的事情,以感受这个地方。然而,如果我只有一天的时间,并且我想快速享受这座城市最好的一面,我会参加有主题的导游团。例如,我有一些很棒的纪念品,是在导游的带领下参观芝加哥古老的摩天大楼的,导游知道如何带我们进入历史悠久的大厅,享受早期灯泡特有的微弱光线。一年后,我享受到了从河上乘船游览芝加哥的建筑,这是真正了解这座城市的另一种方式。在柏林,我预订了一次专门参观柏林街头艺术的旅游团,这让我大开眼界。对我来说,我每天看到的同样的街头艺术,在没有真正注意到的情况下,当放在一个有导游提示的背景下时,就会获得另一个维度。

It is easier to quickly discover the best of a new place with a guided tour or a sightseeing map. In a city you have never visited before, you can explore randomly, hoping to bump into something interesting. This is something I love to do during an afternoon within a longer stay, to get a feel of the place. However, if I have only one day and I want to quickly enjoy the best of the city, I take a guided tour with a theme. For example, I have excellent souvenirs from a guided tour of the old sky-scrapers in Chicago, where the guide knew how to get us into the historical lobbies to enjoy the low light that was typical of early light bulbs. One year later, I enjoyed an architecture boat tour of Chicago, from the river, which is another way to really grasp the city. In Berlin, I booked a tour dedicated to Berlin’s street art, which was eye opening. For me, the same street art I see every day without really noticing gains another dimension when put in a context with one hint from a guide.

但导游服务每周只有几天在固定时间开始,通常需要几个小时,而且可能很昂贵。如果你碰巧在错误的日子经过一座城市,那你就不走运了。但您仍然可以获得旅游地图或印刷版导游。当然,可能有一个应用程序可以做到这一点!许多应用程序提供导游和观光地图,并按景点、饮食、舞蹈和音乐会等主题分类。在芝加哥,建筑协会也在传单上提供免费的建筑之旅。互联网上有很多可以帮助您规划游览的资源,例如“必看景点前 20 名”、“帮助您规划游览的行程”和“在伦敦要做的 101 件事”。

But guided tours start at a fixed hour on a few days a week only, often take a few hours, and may be expensive. If you happen to pass through a city on the wrong day, you are out of luck. But you can still get a tourist map or printed guided tours. And, of course, there is probably an app for that! Plenty of apps provide guided tours and sightseeing maps, classified by themes such as attractions, eat, drink, dance, and concerts. In Chicago, the Society of Architecture offers free architecture tours on leaflets, too. And the Internet is full of resources to help plan a visit, such as “Top 20 List of Must-See Highlights,” “Itineraries to Help You Plan Your Visit,” and “101 Things to Do in London.”

笔记

Note

有时这些资源有点太过分了,例如,在导游带领的“伦敦不寻常和原创的事情”中,其中有一个在公共厕所停下来喝咖啡的地方:正如Timeout London所说,“别担心,这些美丽的东西在摆放蛋糕盘之前,将维多利亚时代的旧厕所改建而成的厕所进行了彻底的擦洗。attendant 于 2013 年开业,里面有一小堆桌子,里面的陶瓷小便池曾经为镇上的男士们提供了缓解。” 3

Sometimes these resources go a bit too far, as in the guided tour Unusual and Original Things to Do in London, for example, which has a stop for coffee in a public loo: As Timeout London says, “Don’t worry, these beautifully converted old Victorian toilets were given a good scrub down before the plates of cakes were laid out. Opened in 2013, Attendant has a small bank of tables where the porcelain urinals once provided relief to gents about town.”3

3. Timeout London,“在伦敦要做的不寻常和原创的事情”, http://www.timeout.com/london/things-to-do/101-things-to-do-in-london-unusual-and-独特的

3.Timeout London, “Unusual and Original Things to Do in London,” http://www.timeout.com/london/things-to-do/101-things-to-do-in-london-unusual-and-unique

熟悉代码库的过程可以类似于熟悉城市的过程。发现它的最好方法是与另一个人——同事一起。但如果你想提供人工导游的替代方案,你可以从旅游业中汲取灵感,提供导游行程和观光地图。这个旅游比喻来自 Simon Brown,他撰写了博客“Coding the Architecture”,还撰写了《Software Architecture for Developers, Volume 2》一书。

The process of becoming familiar with a code base can be similar to the process of becoming familiar with a city. The best way for someone to discover it is with another human—a colleague. But if you want to provide an alternative to a human guide, you can take inspiration from the tourism industry and provide itineraries of guided tours and sightseeing maps. This tourism metaphor comes from Simon Brown, who writes the blog “Coding the Architecture” and also wrote the book Software Architecture for Developers, Volume 2.

需要认识到的一件重要的事情是,一个城市的所有旅游指南都是经过精心策划的:出于各种原因,从不同地标的历史重要性到与金钱相关的更多内容,只呈现了该城市所有可能内容的一小部分。原因。

One important thing to realize is that all the tourism guidance in a city is highly curated: Only a very small subset of all the possible content of the city is presented, for various reasons ranging from the historical importance of different landmarks to more money-related reasons.

代码库和城市之间的一个重要区别是代码库的更改比大多数城市更频繁。因此,提供指南时必须尽量减少更新指南的工作量;当然,自动化在这里是一个不错的选择。

One important difference between a code base and a city is that a code base can change more frequently than most cities. As a result, the guidance must be provided in such a way that the work to keep it up-to-date is minimized; of course, automation is a good option here.

因此:提供精心策划的代码库指南,每个指南都有一个大主题。使用有关导游或观光地图的额外元数据来增强代码,并建立一个自动化机制,以便根据需要根据这些元数据发布更新的指南。基于代码中标签的观光地图或导游是增强代码方法的完美示例。

Therefore: Provide curated guides of a code base, each with a big theme. Augment the code with extra metadata about the guided tour or a sightseeing map, and set up an automated mechanism to publish as often as desired an updated guide from these metadata. A sightseeing map or a guided tour based on tags in the code is a perfect example of the augmented code approach.

如果代码库没有太大变化,导游或观光地图可以像带有所选景点列表的书签一样简单,每个景点都有简短的描述和代码中其位置的链接。如果代码位于 GitHub 这样的平台上,则很容易直接链接到任何代码行。此书签可以以 HTML、Markdown、JSON、专用书签格式或您喜欢的任何其他形式创建。

If the code base does not change much, a guided tour or a sightseeing map can be as simple as a bookmark with a list of the selected places of interest, each with a short description and a link to its location in the code. If the code is on a platform like GitHub, it is easy to link to any line of code directly. This bookmark can be created in HTML, Markdown, JSON, a dedicated bookmark format, or any other form you like.

如果代码库经常更改或可能经常更改,则手动管理的书签需要花费太多精力才能保持最新状态,因此您可以选择动态管理:将标签放置在代码中的选定位置并依赖于搜索IDE 的功能可立即显示书签。如果需要,您可以将元数据添加到标签中,只需扫描代码库即可重建完整的导游。

If the code base changes frequently or may change frequently, a manually managed bookmark would require too much effort to keep up-to-date, so you might choose dynamic curation instead: Place tags on the selected locations in the code and rely on the search features of the IDE to instantly display the bookmarks. If needed, you can add metadata to the tags to enable reconstruction of the complete guided tour, simply by scanning the code base.

您可能担心在代码中添加有关观光地图或导游的标签会污染代码 - 您是对的。这些标签本质上并不是关于标记元素,而是关于它的使用方式,因此请谨慎使用这种方法。

You might be worrying that adding tags about sightseeing maps or guided tours into the code will pollute the code—and you are right. These tags are not really about the tagged element intrinsically but about how it is used, so use this approach sparingly.

将您的代码库视为您徒步旅行的山中美丽的荒野。这里是保护区,石头上、树上直接画着红白相间的远足步道标志。这种涂料确实对自然环境造成了一点污染,但我们都接受它,因为它非常有用,并且对景观的影响有限。

Consider your code base as a beautiful wilderness in the mountains where you go hiking. It is a protected area, and there are red-and-white hiking trail signs painted directly on the stones and on the trees. This paint does pollute the natural environment in a small way, but we all accept it because it’s very useful and degrades the landscape only a limited amount.

创建观光地图

Creating a Sightseeing Map

要创建观光地图,您首先要创建自定义注释或属性,然后将其放在您想要强调的几个最重要的地方。为了提高效率,您应该保持较少的兴趣点数量——最好是 5 到 7 个,当然不超过 10 个。

To create a sightseeing map, you first create a custom annotation or attribute, and then you put it on the few most important places that you want to emphasize. To be effective, you should keep the number of places of interest low—ideally 5 to 7 and certainly no more than 10.

这里最困难的决定之一很可能是命名每个注释或属性。以下是一些命名建议:

It may well be that one of the most difficult decision here is to name each annotation or attribute. Here are some naming suggestions:

  • KeyLandmark或者Landmark

  • KeyLandmark or Landmark

  • MustSee

  • MustSee

  • SightSeeingSite

  • SightSeeingSite

  • CoreConcept或者CoreProcess

  • CoreConcept or CoreProcess

  • PlaceOfInterest, PointOfInterest, 或POI

  • PlaceOfInterest, PointOfInterest, or POI

  • TopAttraction

  • TopAttraction

  • VIPCode

  • VIPCode

  • KeyAlgorithm或者KeyCalculation

  • KeyAlgorithm or KeyCalculation

为了使该方法有用,您还需要确保每个人都了解标签以及如何搜索它们。

For the approach to be useful, you also need to make sure everybody knows about the tags and how to search them.

C# 和 Java 中的观光地图示例

假设在创建自定义属性时,您决定将其放入自己的程序集中以供其他 Visual Studio 项目共享(这也意味着您不希望任何内容特定于其中的任何特定项目)。该属性在 C# 中的外观如下:

Say that in creating a custom attribute, you decide to put it into its own assembly to be shared by other Visual Studio projects (which also means you don’t want anything to be specific to any particular project there). Here is how the attribute might look in C#:

1 公共类KeyLandmarkAttribute:属性
2 {
3 }
1  public class KeyLandmarkAttribute: Attribute
2  {
3  }

您现在可以立即使用此属性来标记您的代码:

You can now immediately use this attribute to tag your code:

1 公共课 Foo
2 {
3 [KeyLandmark("丰富客户的主要步骤
4 从最初的订单购买到准备确认
5 购买")]
6 public void Enrich(CustomerPurchase cp)
7 {
8 //...这里有趣的东西
9 }
10}
1  public class Foo
2  {
3     [KeyLandmark("The main steps of enriching the Customer
4     Purchase from the initial order to a ready-to-confirm
5     purchase")]
6     public void Enrich(CustomerPurchase cp)
7     {
8        //... interesting stuff here
9     }
10 }

Java 和 C# 非常相似。这是同样的示例,现在是 Java 版本:

Java and C# are very similar. Here’s the same example, now in Java:

1 个包 acme.documentation.annotations;
2
3   /** 
4* 在代码中将此位置标记为兴趣点
值得在观光地图上列出。
5 */
6
7 @Retention(RetentionPolicy.RUNTIME)
8 @记录
9 公共@接口兴趣点{
10
11 字符串description()默认“”;
12}
1  package acme.documentation.annotations;
2
3  /**
4 * Marks this place in the code as a point of interest
worth listing on a sightseeing map.
5 */
6
7  @Retention(RetentionPolicy.RUNTIME)
8  @Documented
9  public @interface PointOfInterest {
10
11  String description() default "";
12  }

现在我们可以按如下方式使用它:

And now we can use it as follows:

1 @PointOfInterest("关键计算")
2 私人双重定价(ExoticDerivative ...){
3 ...
1  @PointOfInterest("Key calculation")
2  private double pricing(ExoticDerivative ...){
3  ...

另一种命名可能如下所示:

An alternative naming could look like this:

1 @SightSeeingSite(“这是我们的秘密武器”)
2 公共 SupplyChainAllocation 优化(库存 ...){
3 ...
1  @SightSeeingSite("This is our secret sauce")
2  public SupplyChainAllocation optimize(Inventory ...){
3  ...

在 C# 中,您将使用自定义属性,如下所示:

In C# you would use the custom attribute as follows:

1 公共类 CoreConceptAttribute :属性
2
3 [CoreConcept("丰富客户的主要步骤
4 从最初的订单购买到准备发货
5 发货请求")]
1 public class CoreConceptAttribute : Attribute
2
3 [CoreConcept("The main steps of enriching the Customer
4 Purchase from the initial order to the ready to ship
5 Shipment Request")]

措辞由您决定,您可以使用具有通用名称的通用注释,例如PointOfInterest并添加参数Key calculation以准确说明其含义。或者,您可以决定为每种兴趣点创建一个注释:

The wording is up to you, and you can use one generic annotation with a generic name like PointOfInterest and add the parameter Key calculation to tell precisely what it is about. Alternatively, you could decide to create one annotation for each kind of point of interest:

1 @KeyCalculation()
2 私人双重定价(ExoticDerivative ...){
3 ...
1  @KeyCalculation()
2  private double pricing(ExoticDerivative ...){
3  ...

创建导游

Creating a Guided Tour

在本节所示的示例中,想法是带领新手沿着传入交易的完整处理链,从事件消息队列上的侦听器将传出报告存储到数据库。请注意,尽管它严格分离了域逻辑和基础设施逻辑,但本导览跨越了业务逻辑元素和底层基础设施元素,以便全面了解完整的执行路径。

In the example shown in this section, the idea is to take a newcomer by the hand along the complete chain of processing of an incoming transaction, from the event listener on a message queue down to storing the outgoing report to the database. Note that even though it strictly separates the domain logic and the infrastructure logic, this guided tour spans both business logic elements with elements of the underlying infrastructure in order to give a complete picture of a complete execution path.

该导览当前有六个步骤,每个步骤都锚定在一个代码元素上,该代码元素可以是类、方法、字段或包。

This guided tour currently has six steps, each of which is anchored on a code element that can be a class, a method, a field, or a package.

@GuidedTour此示例使用带有一些参数的自定义注释:

This example uses the custom annotation @GuidedTour with some parameters:

  • 导游的名称:如果只有一个游览,或者您更喜欢导游的一个注释,则此选项是可选的,例如@QuickDevTour

  • The name of the guided tour: This is optional if there is only one tour, or if you prefer one annotation by guided tour, like @QuickDevTour.

  • 本导览上下文中的步骤描述:这与元素上的 Javadoc 注释相反,后者描述了元素的内容,而不一定描述了它的使用方式。

  • A description of the step in the context of this tour: This is in contrast to the Javadoc comment on the element, which describes the element for what it is and not necessarily for how it is used.

  • 排名:排名可以表示为数字或任何类似的东西,它用于在向访问者展示步骤时对步骤进行排序。

  • A rank: The rank can be expressed as a number or anything comparable, and it is used to order the steps when presenting them to the visitor.

以下是导游的示例:

Here’s an example of a guided tour:

1 /**
2 * 监听传入的加油卡交易
3 * 加油卡提供商的外部系统
4 */
5 @GuidedTour(name = "快速开发者教程",
6 描述=“触发完整的MQ侦听器
7 处理链”,等级 = 1)
8 公共类FuelCardTxListener {
1 /**
2 * Listens to incoming fuel card transactions from the
3 * external system of the Fuel Card Provider
4 */
5 @GuidedTour(name = "Quick Developer Tour",
6     description = "The MQ listener which triggers a full
7 chain of processing", rank = 1)
8 public class FuelCardTxListener {

然后它会执行其他步骤,直到最后一步:

It then goes through other steps, until the last one:

1 @GuidedTour(name = "快速开发者教程",
2 描述=“存储结果的DAO
处理后的 3 份加油卡报告”,排名 = 7)
4 公共类ReportDAO {
5
6 public void save(FuelCardTransactionReport 报告){
7 ...
1 @GuidedTour(name = "Quick Developer Tour",
2     description = "The DAO to store the resulting
3     fuel card reports after processing", rank = 7)
4 public class ReportDAO {
5
6 public void save(FuelCardTransactionReport report){
7 ...

笔记

Note

注意这里的编号不是连续的;它从 1 到 7,但只有 6 个步骤。在良好的旧 BASIC 行编号样式中,您可以将行编号为 10、20、30 等,以便在需要时更容易在其间添加另一个步骤。

Note that the numbering here is not consecutive; it goes from 1 to 7, but there are only 6 steps. In the good old BASIC line numbering style, you would number the lines 10, 20, 30, and so on to make it easier to add another step in between when you want to.

如果您只想为开发人员受众提供简单的兴趣点选择,您可以停在这里并依靠用户搜索自定义注释来让 IDE 呈现整个游览:

If you wanted to provide a simple selection of points of interest only for an audience of developers, you could stop here and rely on the user to do a search of the custom annotation to get the IDE to present the tour as a whole:

1 搜索结果“flottio.annotations.GuidedTour”
6 参考文献:
2
3 flottio.fuelcardmonitoring.domain - (src/main/java/l...)
4 - 燃油卡监控
5 - 监控(FuelCardTransaction、车辆)
6 - 燃料卡交易
7 - FuelCard 交易报告
8
9 flottio.fuelcardmonitoring.infra - (src/main/java/l...)
10 - FuelCardTxListener
11 - 报告DAO
1  Search results for 'flottio.annotations.GuidedTour'
6 References:
2
3  flottio.fuelcardmonitoring.domain - (src/main/java/l...)
4  - FuelCardMonitoring
5  - monitor(FuelCardTransaction, Vehicle)
6  - FuelCardTransaction
7  - FuelCardTransactionReport
8
9  flottio.fuelcardmonitoring.infra - (src/main/java/l...)
10 - FuelCardTxListener
11 - ReportDAO

回顾都在这里了,但是不太漂亮,而且没有顺序。不过,对于开发人员可以按所需顺序探索的一小部分主要地标来说,这可能已经足够了,因此不要低估集成方法的价值,因为它比更复杂的机制更简单并且可能更方便。

The recap is all here, but it is not pretty, and there is no ordering. This could be enough for a small list of the main landmarks that a developer can explore in any order desired, though, so do not discount the value of the integrated approach, as it is much simpler and may be more convenient than more sophisticated mechanisms.

然而,对于从开始到结束按顺序参观的导游来说,第一个案例是不够的。因此,下一步是创建一个活文档,使其成为一个动态导游。

However, this first case is not enough for a guided tour that is meant to be visited in order, from start to finish. So the next step is to create a living document out of it so that it is a living guided tour.

创建生动的导游

Creating a Living Guided Tour

比上一节更进一步,您可以创建一个小机制来扫描代码库,以提取有关导游的每个步骤的信息,并以准备好遵循和的形式生成导游的综合报告。订了行程。

Going further than in the preceding section, you can create a little mechanism to scan the code base to extract the information about each step of the guided tour and produce a synthetic report of the guided tour in the form of a ready-to-follow and ordered itinerary.

FuelCardTxListener

触发完整处理链的 MQ 侦听器。

The MQ listener which triggers a full chain of processing.

侦听来自加油卡提供商的外部系统的传入加油卡交易。

Listens to incoming fuel card transactions from the external system of the fuel card provider.

FuelCardTransaction

传入的加油卡交易。

The incoming fuel card transaction.

由加油卡提供商报告的卡与商家之间的交易。

A transaction, between a card and a merchant, as reported by the fuel card provider.

FuelCardMonitoring

负责所有加油卡监控的服务。

The service that takes care of all the fuel card monitoring.

监控燃油卡的使用情况有助于提高燃油效率并检测燃油泄漏和潜在的驾驶员不当行为。

Monitoring of fuel card use to help improve fuel efficiency and detect fuel leakages and potential driver misbehaviors.

monitor(transaction, vehicle)

对收到的加油卡交易进行所有潜在欺诈检测的方法。

The method that does all the potential fraud detection for an incoming fuel card transaction.

1个公共FuelCardTransactionReport监视器(FuelCardTransaction
2交易,车辆车辆){
3 List<String> issues = new ArrayList<String>();
4
5 verifyFuelQuantity(交易、车辆、问题);
6 verifyVehicleLocation(交易、车辆、问题);
7
8、MonitoringStatus状态
9 = issues.isEmpty() ?已验证:异常;
9 返回新的FuelCardTransactionReport(
10 交易、状态、问题);
11}
1  public FuelCardTransactionReport monitor(FuelCardTransaction
2  transaction, Vehicle vehicle) {
3     List<String> issues = new ArrayList<String>();
4
5     verifyFuelQuantity(transaction, vehicle, issues);
6     verifyVehicleLocation(transaction, vehicle, issues);
7
8  MonitoringStatus status
9     = issues.isEmpty() ? VERIFIED : ANOMALY;
9  return new FuelCardTransactionReport(
10    transaction, status, issues);
11 }
FuelCardTransactionReport

传入加油卡交易的报告。

The report for an incoming fuel card transaction.

一笔交易的加油卡监控报告,包含状态和发现的任何潜在问题。

The fuel card monitoring report for one transaction, with a status and any potential issue found.

ReportDAO

DAO 用于存储处理后生成的加油卡报告。

The DAO to store the resulting fuel card reports after processing.

请注意,在本指南中,每个标题都是指向 GitHub 上相应代码行的链接。当感兴趣的点是一个方法(如该monitor()方法)时,为了方便起见,我会逐字包含来自 GitHub 的代码块。在类似的时尚,当兴趣点是一个类时,如果我觉得方便并且与导游的重点相关,我可能会包括非静态字段和公共方法的概述。

Note that in this guided tour, each title is a link to the corresponding line of code on GitHub. When the point of interest is a method (like the monitor() method), I  include its block of code verbatim from GitHub, for convenience. In a similar fashion, when the point of interest is a class, I might include an outline of the nonstatic fields and the public methods if I find it convenient and relevant to the focus of the guided tour.

为了方便起见,这份生动的导览文档是用 Markdown 生成的。然后像 Maven 站点(或 sbt 或任何其他类似工具)这样的工具可以渲染到网页或任何其他格式。另一种选择(如此处所示)是使用 Java 脚本库在浏览器中呈现 Markdown,这不需要额外的工具链。

This living guided tour document is generated in Markdown, for convenience. Then a tool like Maven site (or sbt or any other similar tool) could do the rendering to a web page or in any other format. An alternative, as shown here, is to use a Java-Script library to render the Markdown in the browser, which requires no additional toolchain.

在导游注释中使用字符串的另一种方法是使用枚举,它同时负责命名、描述和排序。但是,这会将导游每个步骤的描述从带注释的代码移动到枚举类,如下所示:

An alternative to using strings in the guided tour annotations would be to use enums, which take care of naming, descriptions, and ordering at the same time. However, this moves the descriptions of each step of the guided tour from the annotated code to the enum class, as you can see here:

1 公共枚举 PaymentJourneySteps {
2 REST_ENDPOINT("单页应用程序调用此端点
购物车的 ID"),
3 AUTH_FILTER("呼叫正在验证中"),
4 AUDIT_TRAIL("如果出现争议,调用将被审计跟踪
并遵守法规”),
5
6 PAYMENT_SERVICE("现在输入实际要执行的服务
工作”),
7
8 REDIRECT(“付款的响应通过
重定向”);
9
10 私有最终字符串描述;
11}
1 public enum PaymentJourneySteps {
2    REST_ENDPOINT("The single page app call this endpoint with
the id of the shopping cart"),
3    AUTH_FILTER("The call is being authenticated"),
4    AUDIT_TRAIL("The call is audit-trailed in case of dispute
and to comply to regulation"),
5
6    PAYMENT_SERVICE("Now enter the actual service to perform
the job"),
7
8    REDIRECT("The response from the payment is sent through a
redirect");
9
10 private final String description;
11 }

然后将该枚举用作注释中的值:

This enum is then used as the value in the annotation:

1 @PaymentJourney(PaymentJourneySteps.PAYMENT_SERVICE)
2公共类PaymentService...
1  @PaymentJourney(PaymentJourneySteps.PAYMENT_SERVICE)
2  public class PaymentService...
导赏服务的实施

在 Java 中,您可以使用名为 QDox 的类 Doclet 库来完成繁重的实现工作,它允许您访问 Javadoc 注释。如果您不需要 Javadoc,那么任何解析器甚至痛苦反射都可以工作。

In Java you can use a Doclet-like library called QDox to do the implementation grunt work, which allows you to access the Javadoc comments. If you don’t need Javadoc, then any parser and even pain reflection could work.

QDox 扫描 src/main/java 中的每个 Java 文件,并从解析的元素集合中,您可以通过注释进行过滤。当 Java 元素(类、方法、包等)有自定义GuidedTour注释,它包含在导游中。您可以提取注释的参数,还可以提取名称、Javadoc 注释、代码行和其他信息(必要时包括代码本身)。然后,您可以将所有这些转换为每个步骤的 Markdown 片段,存储在按步骤排名标准排序的地图中。这样,当扫描完成时,您可以通过按排名顺序连接所有片段来呈现整个文档。

QDox scans every Java file in src/main/java, and from the collection of parsed elements, you can do the filtering by annotation. When a Java element (class, method, package, and so on) has the custom GuidedTour annotation, it is included in the guided tour. You can extract the parameters of the annotation and also extract the name, Javadoc comment, line of code, and other information (including the code itself, when necessary). You can then turn all that into fragments of Markdown for each step, stored in a map sorted by the step rank criteria. This way, when the scan is done, you can render the whole document by concatenating all the fragments in the rank ordering.

当然,细节决定成败,这种代码很快就会变得毛茸茸的,具体取决于您对最终结果的要求有多高。扫描代码并遍历 Java 或 C# 元模型并不总是那么好。在最坏的情况下,您甚至可能最终得到访客模式。我预计这些实践的更主流采用将导致新的小型库的出现,这些库将处理常见用例的大部分繁重工作。

Of course, the devil is in the details, and this kind of code can quickly grow hairy, depending on how demanding you are with respect to the end result. Scanning code and traversing the Java or C# metamodel is not always nice. In the worst case, you could even end up with a visitor pattern. I expect that more mainstream adoption of these practices will lead to new small libraries which will take care of most of the grunt work for common use cases.

穷人的识字编程

A Poor Man’s Literate Programming

导游让人想起文学编程,但相反:导游不是散文和代码,而是代码和散文。对于观光地图,您只需选择兴趣点并按大主题将它们分组即可。对于导游,您需要设计代码元素的线性顺序。在文学编程中,您还可以讲述一个线性故事,该故事通过代码进行,并最终得到一个解释推理的文档和同时解释相应的软件。

A guided tour is reminiscent of literate programming but in reverse: Instead of having prose with code, a guided tour has code with prose. For a sightseeing map, you only have to select the points of interest and group them by big themes. For a guided tour, you need to devise a linear ordering of the code elements. In literate programming, you also tell a linear story that progresses through the code and ends up with a document explaining the reasoning and the corresponding software at the same time.

导游或观光地图不仅仅是一个记录问题,也是鼓励您在工作时不断反思自己工作的一种方式。因此,在构建应用程序的早期步行框架后,最好立即记录导游。这样,您将受益于在工作的同时做文档的深思熟虑的效果。

A guided tour or sightseeing map is not just a documentation concern but also a way to encourage continuous reflection on your own work as you do it. It would therefore be a good idea to document a guided tour as soon as you are building the early walking skeleton of the application. This way, you will benefit from the thoughtful effect of doing the documentation at the same time of doing the work.

总结:策展人准备艺术展览

Summing Up: The Curator Preparing an Art Exhibition

作为对生活策展这个话题的总结,现在让我们回到策展人在艺术展览中的做法,如图5.4所示。

As a concluding note on the topic of living curation, let’s now go back to the approach of the curator in art exhibition, as illustrated in Figure 5.4.

图为博物馆里一个名为“馆长”的卡通人物。

图5.4 博物馆馆长

Figure 5.4 The curator in the museum

展览的策展人主要决定关键的编辑焦点,这通常成为活动的标题。有时焦点是微不足道的,例如“超现实主义者克劳德·莫奈”,但即使在这种情况下,也有一个固执己见的决定——将尚未属于超现实主义的艺术家的现有艺术排除在外。同样,任何文档计划都必须明确传达一个关键信息。

The curator of an exhibition primarily decides on a key editorial focus, which often becomes the title of the event. Sometimes the focus is trivial, such as “Claude Monet, the Surrealist,” but even in this case, there is an opiniated decision—to exclude prior art from the artist that was not yet Surrealism. Similarly, any documentation initiative must clearly deliver one key message.

好的展览试图带来惊喜,以引起兴趣(例如,“你一直认为康定斯基的画作是完全抽象的,但我们将展示抽象形状是如何从他之前的具象画作演变而来的。”)。参观者来这里不仅是为了欣赏艺术品,也是为了扩展他们的文化意识,更好地了解艺术家、艺术品及其时代之间的关系。同样,好的文档可以通过提供不同的事物视角来增加价值和新知识,并强调关系。

Good exhibitions try to bring an element of surprise to create interest (for example, “You’ve always thought Kandinsky paintings are fully abstract, but we’ll show how the abstract shapes evolved from his prior figurative paintings.”). Visitors come not just to see the art pieces but also to expand their cultural awareness and better understand relationships between artists, art pieces, and their era. Similarly, good documentation adds value and new knowledge, with emphasis on relationships, by offering a different perspective of things.

选择和组织现有知识

Selecting and Organizing Existing Knowledge

策展人根据所选的编辑重点来选择艺术作品。大多数可用的展品都留在储藏室中,只有少数对展览特别感兴趣的展品才会展出。同样,文档是一种管理活动,涉及确定给定角度中最重要的内容。

The curator selects art works based on the chosen editorial focus. Most of the pieces available are left in the storage room, and only the few pieces of particular interest for the exhibit are on display. Similarly, documentation is a curation activity that involves deciding what’s most important in a given perspective.

策展人决定在每个房间展示哪些作品。一个房间可以围绕一个时期、艺术家生命中的一个阶段或一个主题来组织。艺术作品可以并排展示,以进行它们之间的比较。它们可以按讲述故事的顺序展示,按时间顺序或通过一系列主题。知识组织是为简单的知识集合增添意义的关键工具。我们按命名文件夹、标签或命名约定对元素进行分组。

The curator decides which pieces to display in each room. A room may be organized around a time period, a phase in the life of the artist, or a theme. Art pieces may be displayed side-by-side to suggest comparisons between them. They may be displayed with an ordering that tells a story, chronologically or through a succession of themes. Organization of knowledge is a key tool for adding meaning to a plain collection of pieces of knowledge. We group elements by named folders, tags, or naming conventions.

需要时添加缺少的内容

Adding What’s Missing When Needed

策展人写了一些文字来解释展览每个部分的大思想。她或他还为每件艺术品写了一个小标签,贴在相应艺术品旁边的墙上。同样,文档需要知识扩充,这可以通过注释、DSL 或命名约定来实现。一些有限数量的文本在某些地方也很有用。只要有可能,这些知识就会附加到相关的代码元素中。

The curator writes a few bits of text explaining the big idea of each section of the exhibition. She or he also writes a small label for each piece of art that is displayed on the wall directly next to the appropriate art piece. Similarly, documentation needs knowledge augmentation, which can occur through annotations, DSL, or naming conventions. Some limited amount of text can be useful in some places, too. This knowledge is attached to the related code elements whenever possible.

当一件被认为对艺术展览至关重要的作品不在馆藏中时,它就会被艺术家借用或委托创作。艺术家还可以直接为其作品的组织做出贡献。

When a work considered essential for the art exhibition is not in the collection, it is borrowed or commissioned from the artist. The artist may also contribute to the organization of his or her pieces directly.

有时会丢失一些信息。策展人可以让研究人员进行调查,或者要求对这幅画进行化学分析,或者通过查看书面档案来找到拼图中缺失的部分。例如,卢浮宫利用对画布上的涂色风格的研究结果,来告诉参观者拉斐尔在他的每幅画作中真正参与了多少。而且这表明这位著名大师并没有接触过其中的很多人!类似地,文档是一种反馈机制,可以帮助您注意到代码或相关知识中缺少或错误的内容。

Sometimes some information is missing. The curator can have researchers conduct investigations or may request chemical analysis on the painting or by looking at written archives to find the missing piece in the puzzle. For example, the Louvre uses research results on the style of brushing colors on the canvas in order to tell visitors how much Raphael really participated in each of his paintings. And it reveals that the famous master did not touch many of them! In a similar way, documentation is a feedback mechanism that helps you notice when something is missing or wrong in the code or in the related knowledge.

为无法参加的人和后代提供便利

Accessibility for People Who Can’t Attend and for Posterity

策展人创建了展览目录,其中概括了所有展示的内容:按部分的解释性文字、作为优质图片的艺术作品及其标签。作为一本书的目录的组织方式通常类似于展览场地中房间的组织方式。

The curator creates a catalog of the exhibition, which recaps all the content displayed: the explanative text by section, the art pieces as quality pictures, and their labels. The catalog as a book is usually organized in a way that is similar to the organization of the rooms in the exhibition venue.

博物馆现在有时会提供昂贵且重量级的完整展览目录,它们也提供较短形式的目录,仅包含主要作品的摘要。我通常购买较短的目录,到目前为止,这是一个更有吸引力的阅读!

Museums now sometimes offer expensive and heavyweight complete exhibition catalogs, and they also offer catalogs in a shorter form, with just a digest of the major pieces. I usually buy the shorter catalog, which is a more attractive read by far!

文档还涉及使知识易于访问并确保重要的部分在未来得到保留。例如,您可以将内容作为文档发布在交互式网站上,针对不同的受众和不同的需求,就像艺术博物馆发布的不同目录一样。

Documentation also involves making knowledge accessible and ensuring that the important pieces are persisted for the future. You may, for example, publish content as documents and on an interactive website, targeted for different audiences and different needs—much like the different catalogs published by the art museum.

概括

Summary

因为任何现实生活中的代码库中的典型知识量都是巨大的,所以任何利用它的尝试都需要通过一个管理过程来丢弃大部分知识,而这个过程本身通过关注要点来为所管理的知识增加价值。

Because the typical quantity of knowledge in any real-life code base is huge, any attempt at making use of it involves discarding most of it, through a process of curation that itself adds value to the curated knowledge by focusing on the essentials.

活生生的策展、鼓舞人心的范例、突出核心内容、提供导游和观光地图是一些可能的策展方法,可以突出特定目的的知识子集。

Living curation, inspiring exemplars, highlighting the core, and providing guided tours and sightseeing maps are some possible approaches to curation that can highlight a subset of knowledge for a particular purpose.

第6章

Chapter 6

自动化文档编制

Automating Documentation

如前所述,活文档不一定需要生成正式文档来处理知识。然而,在许多情况下,需要生成具有传统外观的文档。在这种情况下,真正“活的”文档的最明显的例子是与它们所描述的知识完全相同的发展速度的文档。您需要自动化来使实时文档成为可能。

As described earlier, living documentation does not necessarily require producing formal documents in order to deal with knowledge. However, there are a number of situations in which it is desirable to produce traditional-looking documents. In such a case, the most obvious example of documentation that is really “living” is documents that evolve at exactly the same pace as the knowledge they describe. You need automation to make living documents possible.

本章介绍两个重要的相关概念:使用自动化帮助创建活文档

This chapter introduces two important, related concepts: using automation to help create living documents.

活文件

Living Documents

活文档是与其描述的系统同步发展的文档。手动创建活文档非常耗时,因此活文档通常是通过自动化来实现的。

A living document is a document that is evolving at the same pace as the system it describes. It’s prohibitively time-consuming to create a living document manually, so a living document is usually achieved through automation.

顾名思义,活文档在很大程度上依赖于活文档,当其他文档方式无法跟上变化的步伐,或者目标受众无法访问时,就需要活文档。

As the names suggests, living documentation relies a lot on living documents, which are required when other means of documentation cannot keep up with the pace of change, or are not accessible for the intended audience.

活文档就像报告工具一样,每次更改后都会生成新报告。更改通常是代码更改,但也可能是对话期间做出的关键决定。

A living document works like a reporting tool that produces a new report after each change. A change is usually a code change but could also be a key decision made during a conversation.

本章介绍了活文档的一些关键示例,包括动态术语表和动态图表。

This chapter presents a few key examples of living documents, including living glossaries and living diagrams.

创建活文档的步骤

Steps in Creating a Living Document

创建活文档通常涉及四个主要步骤:

Creating a living document typically involves four main steps:

  1. 选择存储在某处的一系列数据,例如源代码管理中的源代码。

  2. Select a range of data stored somewhere, such as source code in source control.

  3. 根据文档的目标过滤数据。

  4. Filter the data according to the objective of the document.

  5. 对于通过过滤器的每条数据,提取文档感兴趣的内容子集。它可以被看作是一个投影,并且它特定于图表的目的。

  6. For each piece of data that made it out through the filter, extract the subset of its content that is of interest for the document. It can be seen as a projection, and it’s specific to the purpose of the diagram.

  7. 将数据和数据中的关系转换为目标格式以生成文档。对于视觉文档,该目标可以是对渲染库的 API 的一系列调用。对于文本文档,它可以是生成 PDF 的工具使用的文本片段列表。

  8. Convert the data and the relationships in the data into the target format to produce the document. For a visual document, this target can be a sequence of calls to the API of the rendering library. For a text document, it can be a list of text snippets consumed by a tool to produce a PDF.

如果渲染非常复杂,则转换为另一个模型的步骤可能会多次 - 创建中间模型,然后链接这些模型来驱动最终的渲染库。

If the rendering is very complex, the step of converting into another model may be multiple times—to create intermediate models that are then chained to drive the final rendering library.

每个步骤中最困难的部分是编辑视角和演示规则之间的相互作用。应该选择或忽略哪些数据?应该从其他来源添加哪些信息?应该使用什么布局?

The hard part in each step is the interplay between the editorial perspective and the presentation rules. What data should be selected or ignored? What information should be added from another source? What layout should be used?

演示规则

Presentation Rules

一份好的文档必须遵循特定的规则,例如一次显示或列出不超过五到九个项目。还有选择特定布局(例如列表、表格或图表)的规则,以使其与问题的结构一致。这不是一本关于该主题的书,但是对此类演示规则的一些了解将帮助您提高文档的效率。

A good document must follow particular rules, such as showing or listing no more than five to nine items at a time. There are also rules for choosing a particular layout—such as a list or a table or a chart—so that it is congruent with the structure of the problem. This is not a book on that topic, but some awareness of such presentation rules will help you make your documents more efficient.

生活词汇

Living Glossaries

您如何与参与项目的每个人共享该领域的通用语言?通常的答案是提供属于通用语言的每个术语的完整术语表,以及解释您需要了解的内容的描述。然而,通用语言是一种不断发展的生物,因此需要维护术语表,并且与源代码相比,它存在过时的风险。

How do you share the ubiquitous language of the domain with everyone involved in a project? The usual answer is to provide a complete glossary of every term that belongs to the ubiquitous language, together with a description that explains what you need to know about it. However, the ubiquitous language is an evolving creature, so the glossary needs to be maintained, and there is a risk that it will become outdated compared to the source code.

在领域模型中,代码代表业务领域,尽可能接近领域专家思考和谈论它的方式。在领域模型中,优秀的代码告诉领域业务:每个类名、每个方法名、每个枚举常量名和每个接口名都是该领域通用语言的一部分。但并不是每个人都能读懂代码,而且几乎总是有一些代码与领域模型关系不大。

In a domain model, the code represents the business domain, as closely as possible to the way the domain experts think and talk about it. In a domain model, great code tells the domain business: Each class name, each method name, each enum constant name, and each interface name is part of the ubiquitous language of the domain. But not everyone can read code, and there is almost always some code that is not very related to the domain model.

因此:从源代码中提取通用语言的术语表。将源代码视为唯一的事实来源,并在每个类、接口和公共方法代表域概念时非常小心地命名。将领域概念的描述直接添加到源代码中,作为可以通过工具提取的结构化注释。提取术语表时,找到一种方法来过滤掉不表达领域的代码。

Therefore: Extract the glossary of the ubiquitous language from the source code. Consider the source code as the single source of truth and take great care in the naming of each class, interface, and public method whenever they represent domain concepts. Add the description of the domain concept directly to the source code, as structured comments that can be extracted by a tool. When extracting the glossary, find a way to filter out code that is not expressing the domain.

如图6.1所示,实时词汇表处理器扫描源代码及其注释以生成实时词汇表,该词汇表将保持最新状态,因为它可以根据需要频繁地重新生成。

As illustrated on Figure 6.1, the living glossary processor scans the source code and its annotations to generate a living glossary that will remain up-to-date because it can be regenerated as frequently as desired.

下图显示了实时词汇表的概述。 标题为“生活图”的图显示了通过生活图处理器传输到“始终保持最新”的“生活图”的源代码和注释。

图 6.1 实时词汇表概述

Figure 6.1 Overview of a living glossary

对于一个成功的实时术语表,代码必须是声明性的。代码越像业务领域的 DSL,词汇表就越好。事实上,对于开发人员来说,不需要动态术语表,因为术语表就是代码本身。动态术语表对于无法访问 IDE 中的源核心的非开发人员特别有用。它带来了额外的便利,因为所有内容都在一个文档中。

For a successful living glossary, the code must be declarative. The more the code looks like a DSL of the business domain, the better the glossary. Indeed, for developers there is no need for a living glossary because the glossary is the code itself. A living glossary is especially useful for nondevelopers who don’t have access to the source core in an IDE. It brings additional convenience in being all in a single document.

活生生的词汇表也是一种反馈机制。如果术语表看起来不太好,或者您发现很难使术语表发挥作用,那么您就知道代码中有一些需要改进的地方。

A living glossary is also a feedback mechanism. If a glossary does not look good, or if you find it hard to make the glossary work, you know you have something to improve in the code.

生活词汇表如何运作

How a Living Glossary Works

在许多语言中,文档可以作为结构化注释直接嵌入代码中,并且最好编写类、接口或重要方法的描述。然后,Javadoc 等工具可以提取注释并根据它们创建报告。使用Javadoc您可以根据提供的Doclet创建您自己的Doclet(文档生成器),并且不需要花费很多精力。通过使用自定义 Doclet,您可以以任何格式导出自定义文档。

In many languages, documentation can be embedded directly within the code as structured comments, and it is good practice to write a description of what a class, an interface, or an important method is about. Tools like Javadoc can then extract the comments and create a report based on them. With Javadoc you can create your own Doclet (documentation generator) based on the provided Doclet, and it does not take a lot of effort. By using a custom Doclet, you can export custom documentation in any format.

Java 中的注释和 C# 中的属性非常适合增强代码。例如,您可以使用自定义域构造型(@DomainService@DomainEvent@BusinessPolicy等)或与域无关的构造型(@AbstractFactory@Adapter等)来注释类和接口。这使得过滤掉那些对表达领域语言没有贡献的类变得很容易。当然,您需要创建一个小型注释库来增强您的代码。

Annotations in Java and attributes in C# are great for augmenting code. For example, you can annotate classes and interfaces with custom domain stereotypes (@DomainService, @DomainEvent, @BusinessPolicy, and so on) or domain-irrelevant stereotypes (@AbstractFactory, @Adapter, and so on). This makes it easy to filter out classes that do not contribute to expressing the domain language. Of course, you need to create a small library of annotations to augment your code.

如果做得好,这些注释也表达了编写代码的开发人员的意图。它们是刻意练习的一部分。

If done well, these annotations also express the intention of the developer who wrote the code. They are part of a deliberate practice.

过去,我曾使用刚才描述的方法来提取参考业务文档,然后我可以将其直接发送给国外客户。我使用自定义 Doclet 导出 Excel 电子表格,其中每个类别的业务域概念都有一个选项卡。这些类别只是基于添加到代码中的自定义注释。

In the past I have used the approach just described to extract a reference business document that I could then send directly to a customer abroad. I was using a custom Doclet to export an Excel spreadsheet with one tab for each category of business domain concepts. The categories were simply based on the custom annotations added to the code.

请举个例子!

An Example Please!

让我们看一个关于小猫的生动词汇表的简短且过于简单的示例,因为每个人都喜欢小猫。以下伪代码代码库代表了猫的主要活动:

Let’s look at a brief and oversimplified example of a living glossary about a kitten, because everybody loves kittens. The following code base in pseudo-code represents the main activities of a cat:

1 个模块 com.acme.catstate
2
3   // 猫的主要活动集合
4 @CoreConcept
5. 接口CatActivity
6
7   // 猫如何改变其活动以响应事件
8 @CoreBehavior
9 @状态机
10 CatState nextState(事件)
11
12 // 猫闭着两只眼睛睡觉
13 类 睡眠 -|> CatActivity
14
15 // 猫正在吃东西,或者离菜很近
16 类 吃 -|> CatActivity
17 号
18 // 猫睁大眼睛积极追逐
19 类追逐 -|> CatActivity
20
21 @CoreConcept
22 class Event // 任何可能发生的对猫来说重要的事情
23 无效应用(对象)
24
25 类 Timestamp // 技术样板
1  module com.acme.catstate
2
3  // The set of the main activities of a cat
4  @CoreConcept
5  interface CatActivity
6
7  // How the cat changes its activity in response to an event
8  @CoreBehavior
9  @StateMachine
10 CatState nextState(Event)
11
12 // The cat is sleeping with its two eyes closed
13 class Sleeping -|> CatActivity
14
15 // The cat is eating, or very close to the dish
16 class Eating -|> CatActivity
17
18 // The cat is actively chasing, eyes wide open
19 class Chasing -|> CatActivity
20
21 @CoreConcept
22 class Event // Anything  that can happen that matters to the cat
23 void apply(Object)
24
25 class Timestamp // technical boilerplate

这只是简单的源代码,描述了猫的日常生活领域。然而,它通过注释进行了增强,突出显示了该领域中重要的内容。

This is just plain source code that describes the domain of the daily life of a cat. However, it is augmented with annotations that highlight what’s important in the domain.

根据此代码构建动态术语表的处理器将打印如下术语表:

A processor that builds a living glossary out of this code will print a glossary like the following:

1 术语表
2 --------
3
4 CatActivity:猫的主要活动的集合。
5 - 睡觉:猫闭着两只眼睛睡觉
6 - 吃东西:猫正在吃东西,或者非常接近盘子
7 - 追逐:猫睁大眼睛积极追逐
8
9 nextState:猫如何改变其活动以响应
10 一个事件
11
12 事件:任何可能发生的对猫来说重要的事情
1  Glossary
2  --------
3
4  CatActivity: The set of the main activities of a cat.
5  - Sleeping: The cat is sleeping with its two eyes closed
6  - Eating: The cat is eating, or very close to the dish
7  - Chasing: The cat is actively chasing, eyes wide open
8
9  nextState: How the cat changes its activity in response to
10 an event
11
12 Event: Anything that can happen that matters to the cat

请注意,这里忽略了Timestamp类和方法,因为它们对于术语表来说并不重要。Event此外,实现的每个单独的类都CatActivity与它们实现的接口一起呈现,因为这是我们考虑特定构造的方式。

Notice that the Timestamp class and the Event method have been ignored here because they don’t matter for the glossary. Also, each separate class that implements CatActivity has been presented together with the interface they implement, because that’s the way we think about that particular construction.

笔记

Note

这是状态设计模式,在这里它真正是业务领域的一部分。

This is the state design pattern, and here it is genuinely part of the business domain.

从代码中构建词汇表本身并不是目的;从第一个生成的术语表中,您可能会注意到该条目nextState并不像您期望的那么清晰。(这在术语表中比在代码中更明显。)因此,您返回到代码并重命名该方法nextActivity()

Building the glossary out of the code is not an end to itself; from this first generated glossary you might notice that the entry nextState is not as clear as you’d expect it to be. (This is more visible in the glossary than in the code.) So you go back to the code and rename the method nextActivity().

一旦您重建项目,术语表就会更新,因为它毕竟是一个动态术语表:

As soon as you rebuild the project, the glossary is updated because it is, after all, a living glossary:

1 术语表
2 --------
3
4 CatActivity:猫的主要活动的集合。
5 - 睡觉:猫闭着两只眼睛睡觉
6 - 吃东西:猫正在吃东西,或者非常接近盘子
7 - 追逐:猫睁大眼睛积极追逐
8
9 nextActivity:猫如何改变其活动作为响应
10 参加活动
11
12 事件:任何可能发生的对猫来说重要的事情
1  Glossary
2  --------
3
4  CatActivity: The set of the main activities of a cat.
5  - Sleeping: The cat is sleeping with its two eyes closed
6  - Eating: The cat is eating, or very close to the dish
7  - Chasing: The cat is actively chasing, eyes wide open
8
9  nextActivity: How the cat changes its activity in response
10 to an event
11
12 Event: Anything that can happen that matters to the cat

活文档的信息管理

Information Curation for Living Documents

刚刚描述的技术需要编程语言的解析器,并且解析器不能忽略注释。对于 Java,有很多选择,包括 Antlr、JavaCC、Java 注释处理 API 和一些开源工具。然而,最简单的选择是使用自定义 Doclet,这就是此处描述的方法。

The technique just described requires a parser for the programming language, and the parser must not ignore the comments. For Java, there are many options, including like Antlr, JavaCC, Java annotation-processing APIs, and several open-source tools. However, the simplest option is to go with a custom Doclet, and this is the approach described here.

笔记

Note

即使您不关心 Java,您仍然可以继续阅读;这里的重要信息很大程度上与语言无关。

Even if you don’t care about Java, you can still read on; the important information here is largely language agnostic.

在仅涵盖一个领域的简单项目中,一个术语表就足够了。Doclet 被赋予了 Javadoc 元模型的根,并且从这个根开始它扫描所有编程元素,包括类、接口和枚举。

In a simple project that covers only one domain, one single glossary is enough. The Doclet is given the root of the Javadoc metamodel, and from this root it scans all programming elements, including classes, interfaces, and enums.

对于每个类别,主要问题是“这对企业来说是否足够重要,足以包含在术语表中?” Java 注释可以在很大程度上回答这个问题。如果您使用“具有商业意义”的注释,则具有此注释的每个类都是词汇表的有力候选者。

For each class, the main question is “Does this matter to the business enough to be included in the glossary?” Java annotations can go a long way toward answering this question. If you use a “business meaningful” annotation, each class that has this annotation is a strong candidate for the glossary.

警告

Caution

最好避免处理注释的代码和注释本身之间的强耦合。为了避免这种耦合,可以仅通过注释的前缀(例如,org.livingdocumentation.*)或其非限定名称(例如,BusinessPolicy)来识别注释。另一种方法是检查本身由元注释注释的注释,例如@LivingDocumentation. 这样的元注释本身可以通过简单的名称来识别,以避免直接耦合。

It is preferable to avoid strong coupling between the code that processes annotations and the annotations themselves. To avoid this coupling, annotations can be recognized just by their prefix (for example, org.livingdocumentation.*) or by their unqualified name (for example, BusinessPolicy). Another approach is to check annotations that are themselves annotated by a meta-annotation, such as @LivingDocumentation. Such a meta-annotation can itself be recognized by simple name only to avoid direct coupling.

对于要包含的每个类,Doclet 会深入了解该类的成员,并以适合术语表的方式打印术语表感兴趣的所有内容。

For each class to be included, the Doclet then drills down the members of the class and prints everything that is of interest for the glossary, in a way that is appropriate for the glossary.

有选择地显示和隐藏源代码的相关部分并对相关元素进行分组至关重要。如果不是这样,标准的 Javadoc 就足够了。动态术语表的核心是关于显示什么、隐藏什么以及如何以最合适的方式呈现信息的所有编辑决策。脱离特定环境很难做出这样的决定。我不会一步一步地告诉你如何做到这一点,但我确实给出了一些选择性管理的例子:

Selectively showing and hiding the relevant portions of the source code and grouping the related elements are critically important. If it weren’t for this, the standard Javadoc would be enough. At the core of a living glossary are all the editorial decisions on what to show, what to hide, and how to present the information in the most appropriate way. It’s hard to make such decisions outside a context. I won’t tell how to do it step by step, but I do give some examples of selective curation:

  • 枚举及其常量

  • An enum and its constants

  • bean 及其直接非瞬态字段

  • A bean and its direct non-transient fields

  • 接口、它的直接方法以及它的非技术性和非抽象性的主要子类

  • An interface, its direct methods, and its main subclasses that are not technical and not abstract

  • 值对象及其“操作下封闭”的方法1(即仅涉及类型本身的方法)

  • A value object and its methods that are “closed under operation”1 (that is, methods that only involve the type itself)

1.埃文斯,埃里克。领域驱动设计:解决软件核心的复杂性。Hoboken:Addison-Wesley Professional,2003。请参阅“运营关闭”部分。

1.Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. Hoboken: Addison-Wesley Professional, 2003. See the section “Closure of Operations.”

对于相关的术语表,通常必须隐藏代码中的许多细节:

For a relevant glossary, a lot of details from the code usually must be hidden:

  • 您通常会忽略超级对象中的所有方法,例如toString()equals()

  • You typically ignore all methods from the super-object, such as toString() and equals().

  • 您通常会忽略所有瞬态字段,因为它们只是出于优化目的而对业务几乎没有任何意义。

  • You typically ignore all transient fields because they are there just for optimization purposes and seldom mean anything for the business.

  • 您通常会忽略所有常量字段(类型本身的public static final除外)(如果它们代表业务的重要概念)。

  • You typically ignore all constant fields, except the public static final of the type itself, if they represent important concepts of the business.

  • 标记接口通常不需要列出其子类,对于任何只有一种方法的接口也可能如此。

  • Marker interfaces often don’t need to list their subclasses, and the same may be true for any interface that has only one method.

选择性过滤很大程度上取决于代码的风格。如果常量通常用于隐藏技术文字,那么它们应该大部分被隐藏,但如果它们通常在公共 API 中使用,则词汇表可能会对它们感兴趣。

The selective filtering depends to a large extent on the style of the code. If constants are usually used to hide technical literals, then they should be mostly hidden, but if they are usually used in the public API, they may be of interest for the glossary.

根据代码风格,您可以调整过滤,使其默认完成大部分工作,即使在某些情况下做得太过分。要补充或偏离此默认过滤,您可以使用覆盖机制(例如,通过使用注释)。

Depending on the style of code, you can adjust the filtering so that it does most of the work by default, even if it goes too far in some cases. To supplement or deviate from this default filtering, you can use an override mechanism (for example, by using annotations).

例如,选择性过滤可能会默认忽略所有方法;在这种情况下,您必须定义一个注释来区分应出现在术语表中的方法。但是,我永远不会使用命名的注释,@Glossary因为它在代码上下文中会产生噪音。类或方法并不意味着属于或不属于术语表;它旨在表示或不表示域的概念。但是方法可以表示该领域的核心概念,并@CoreConcept可以使用可用于将该方法包含在术语表中的注释进行注释。

For example, the selective filtering may ignore every method by default; in this case, you have to define an annotation to distinguish the methods that should appear in the glossary. However, I would never use an annotation named @Glossary because it would be noise in the context of the code. A class or method is not meant to belong to a glossary or not; it is meant to represent a concept of the domain or not. But a method can represent a core concept of the domain and be annotated as such with a @CoreConcept annotation that can be used to include the method in the glossary.

有关策展的更多信息,请参阅第 5 章活生生的策展:识别权威知识”。有关如何正确使用注释来为代码添加含义的更多信息,请参阅第 4 章“知识增强”。

For more on curation, see Chapter 5, “Living Curation: Identifying Authoritative Knowledge.” For more on the proper usage of annotations to add meaning to code, refer to Chapter 4, “Knowledge Augmentation.”

在有界上下文中创建术语表

Creating a Glossary Within a Bounded Context

在领域驱动设计中,只有在给定的有界上下文中才能明确地定义通用语言。如果您对有界上下文感到不舒服,请不要担心;对于本次讨论,您可以将术语“有界上下文”替换为“有关一组内聚用例的模块”

In domain-driven design, a ubiquitous language can be defined with no ambiguity only within a given bounded context. If you are not comfortable with bounded contexts, don’t worry; for this discussion you may replace the term bounded context with module about a set of cohesive use-cases.

如果源代码跨越多个有界上下文,则需要按有界上下文分隔术语表。为此,必须显式声明有界上下文。

If the source code spans several bounded contexts, you need to segregate the glossary by bounded context. In order to do that, the bounded contexts must be explicitly declared.

您可以使用注释来声明有界上下文,但这次注释将位于模块上。在 Java 中,它们是包注释,使用伪类package-info.java

You can use annotations to declare the bounded contexts, but this time the  annotations will be on modules. In Java they are package annotations, using the pseudo-class package-info.java:

1 包信息.java
2
3 // 猫有许多有趣的活动,以及它们的方式
4 // 从一种到另一种的切换可以通过马尔可夫来模拟
5 // 链。
6 @BoundedContext(name = "猫活动")
7 包 com.acme.lolcat.domain
1 package-info.java
2
3 // Cats have many fascinating activities, and the way they
4 // switch from one to another can be simulated by Markov
5 // chains.
6 @BoundedContext(name = "Cat Activity")
7 package com.acme.lolcat.domain

这是应用程序中的第一个有界上下文,您还有另一个有界上下文,同样是关于猫,但这次是从不同的角度来看:

This is the first bounded context in the application, and you have another bounded context, again on cats but this time from a different perspective:

1 包信息.java
2
3// 猫的情绪始终是个谜。
4 // 然而我们可以用网络摄像头观察猫并使用图像
5 // 处理检测心情并将其分类为心情
6 // 家庭。
7
8 @BoundedContext(name = "猫心情")
9 包 com.acme.catmood.domain
1  package-info.java
2
3 // Cats moods are always a mystery.
4 // Yet we can observe cats with a webcam and use image
5 // processing to detect moods and classify them into mood
6 // families.
7
8 @BoundedContext(name = "Cat Mood")
9 package com.acme.catmood.domain

对于多个有界上下文,处理会稍微复杂一些,因为每个有界上下文都有一个术语表。您需要清点所有有界上下文,然后将代码的每个元素分配给相应的术语表。如果代码结构良好,则有界上下文在模块的根部明确定义,因此如果类属于特定模块,则它显然属于有界上下文。

With several bounded contexts, the processing is a bit more complicated because there will be one glossary for each bounded context. You need to inventory all the bounded contexts and then assign each element of the code to the corresponding glossary. If the code is well structured, the bounded contexts are clearly defined at the roots of modules, so a class obviously belongs to a bounded context if it belongs to a particular module.

然后处理过程如下:

The processing then proceeds as follows:

  1. 扫描所有包并检测每个上下文。

  2. Scan all packages and detect each context.

  3. 为每个上下文创建一个术语表。

  4. Create a glossary for each context.

  5. 扫描所有类,并针对每个类找出它所属的上下文。这可以简单地通过以模块限定名称(例如,com.acme. catmood.domain.funny.Laughing)开头的限定类名称(例如,com.acme.catmood.domain)来完成。

  6. Scan all classes, and for each class, find out what context it belongs to. This can simply be done from the qualified class name (for example, com.acme. catmood.domain.funny.Laughing) that starts with the module qualified name (for example, com.acme.catmood.domain).

  7. 对于每个术语表,应用上述选择性过滤和管理过程来构建一个漂亮且相关的术语表。

  8. For each glossary, apply the selective filtering and curation process described above for building a nice and relevant glossary.

这个过程可以根据您的口味进行改进。词汇表可以按条目名称排序或按概念的重要性排序。

This process can be enhanced to suit your taste. A glossary may be sorted by entry name or sorted by importance of concepts.

生活词汇表的案例研究

Case Study of a Living Glossary

让我们仔细看看音乐理论和 MIDI 领域的示例项目。图 6.2显示了在 IDE 中打开项目时看到的内容。

Let’s take a close look at a sample project in the domain of music theory and MIDI. Figure 6.2 shows what you see when you open the project in an IDE.

该图显示了“代码库的树形视图”。

图 6.2 代码库的树形视图

Figure 6.2 Tree view of the code base

有两个模块,每个模块包含一个包。每个模块定义一个有界上下文。第一个侧重于西方音乐理论,如图 6.3所示。

There are two modules, each containing a single package. Each module defines a bounded context. The first one, which focuses on Western music theory, is shown in Figure 6.3.

下图显示了“将第一个有界上下文声明为包注释”。 文本解读了西方音乐理论的代表,从音符到和弦、节奏、和声和旋律。 在有界上下文名称“音乐理论”中给出了相应的链接。

图6.3 将第一个有界上下文声明为包注释

Figure 6.3 Declaration of the first bounded context as a package annotation

第二个有界上下文主要关注 MIDI,如图 6.4所示。

The second bounded context, which focuses on MIDI, is shown in Figure 6.4.

下图显示了“将第二个有界上下文声明为包注释”。 文本中写道“代表了创作和录制节奏和旋律序列所需的 midi 概念”。 在有界上下文名称“MIDI排序”中给出了相应的域和链接。

图6.4 将第二个有界上下文声明为包注解

Figure 6.4 Declaration of the second bounded context as a package annotation

在第二个有界上下文中,图 6.5显示了一个简单值对象及其 Javadoc 注释和注释的示例。

From the second bounded context, Figure 6.5 shows an example of a simple value object with its Javadoc comment and annotation.

图中显示了值对象及其注释。 代码读取 package com.martraire.music.midi import org.livingdocumentation.annotation.valueobject;  at value 对象 public interface MidiMessage 左大括号和右大括号。

图 6.5 带有注释的值对象

Figure 6.5 A value object with its annotation

从第一个上下文来看,图 6.6显示了一个枚举的示例,它也是一个值对象,带有它的 Javadoc 注释、对其常量的 Javadoc 注释以及注释。

From the first context, Figure 6.6 shows an example of an enum that is a value object as well, with its Javadoc comments, the Javadoc comments on its constants, and the annotation.

下图显示了一个枚举及其注释。

图 6.6 带有注释的枚举

Figure 6.6 An enum with its annotation

请注意,还有其他方法,但术语表将忽略它们。

Note that there are other methods, but they will be ignored for the glossary.

从某些事情开始并手动调整

要创建实时术语表处理器,您需要创建一个自定义 Doclet,它创建一个文本文件并在 Markdown 中打印术语表标题:

To create the living glossary processor, you need to create a custom Doclet that creates a text file and prints the glossary title in Markdown:

1 公共类 AnnotationDoclet 扩展 Doclet {
2
3 //...
4
5 // doclet 入口点
6 public static boolean start(RootDoc root) {
7 尝试{
8 writer = new PrintWriter("glossary.txt");
9 writer.println("# " + "词汇表");
10个进程(根);
11 writer.close();
12 } catch (FileNotFoundException e) {
13 //...
14}
15 返回真;
16}
1 public class AnnotationDoclet extends Doclet {
2
3    //...
4
5    // doclet entry point
6    public static boolean start(RootDoc root) {
7      try {
8        writer = new PrintWriter("glossary.txt");
9        writer.println("# " + "Glossary");
10       process(root);
11       writer.close();
12     } catch (FileNotFoundException e) {
13       //...
14     }
15     return true;
16  }

剩下要实现的是方法process(),它枚举 Doclet 根中的所有类并检查每个类是否对业务有意义:

What’s left to implement is the method process(), which enumerates all classes from the Doclet root and checks whether each class is meaningful for the business:

1 公共无效过程(){
2 最终 ClassDoc[] 类 = root.classes();
3 for (ClassDoc 类:类) {
4 if (isBusinessMeaningful(cls​​s)) {
5个进程(类);
6 }
7}
8}
1        public void process() {
2                final ClassDoc[] classes = root.classes();
3                for (ClassDoc clss : classes) {
4                        if (isBusinessMeaningful(clss)) {
5                                process(clss);
6                        }
7                }
8        }

如何检查课程对业务是否有意义?这里只能通过注释来完成。在这种情况下,您可以认为所有注释都将org.livingdocumentation.*代码标记为对术语表有意义。这是一个粗略的简化,但已经足够了:

How do you check whether a class is meaningful for the business? Here you do it only through annotation. In this case, you can consider that all annotations from org.livingdocumentation.* mark the code as meaningful for the glossary. This is a gross simplification, but here it’s enough:

1 私有布尔值 isBusinessMeaningful(ProgramElementDoc doc){
2 最终 AnnotationDesc[] 注释 = doc.annotations();
3 for (AnnotationDesc 注释:注释) {
4 if (isBusinessMeaningful(annotation.annotationType())) {
5             返回真;
6 }
7}
8 返回 false;
9 }
10
11 私有布尔值 isBusinessMeaningful(AnnotationTypeDoc
                                        注释类型) {
12 返回annotationType.qualifiedTypeName()
          .startsWith("org.livingdocumentation.annotation.");
13}
1  private boolean isBusinessMeaningful(ProgramElementDoc doc){
2    final AnnotationDesc[] annotations = doc.annotations();
3    for (AnnotationDesc annotation : annotations) {
4      if (isBusinessMeaningful(annotation.annotationType())) {
5             return true;
6      }
7    }
8    return false;
9  }
10
11 private boolean isBusinessMeaningful(AnnotationTypeDoc
                                        annotationType) {
12   return annotationType.qualifiedTypeName()
          .startsWith("org.livingdocumentation.annotation.");
13 }

如果一个类是有意义的,那么你必须在词汇表中打印它:

If a class is meaningful, then you must print it in the glossary:

1 protected void process(ClassDoc clss) {
2 writer.println("");
3 writer.println("## *" + clss.simpleTypeName() + "*");
4 writer.println(clss.commentText());
5 writer.println("");
6 if (clss.isEnum()) {
7 for (FieldDoc 字段: clss.enumConstants()) {
8 printEnumConstant(字段);
9 }
10 writer.println("");
11 for (MethodDoc 方法: clss.methods(false)) {
12 打印方法(方法);
13}
14 } else if (clss.isInterface()) {
15 for (ClassDoc subClass : subclasses(clss)) {
16 printSubClass(子类);
17}
18 } 其他 {
19 for (FieldDoc 字段: clss.fields(false)) {
20 打印字段(字段);
21}
22 for (MethodDoc 方法: clss.methods(false)) {
23 打印方法(方法);
24}
25}
26}
1  protected void process(ClassDoc clss) {
2    writer.println("");
3    writer.println("## *" + clss.simpleTypeName() + "*");
4    writer.println(clss.commentText());
5    writer.println("");
6    if (clss.isEnum()) {
7       for (FieldDoc field : clss.enumConstants()) {
8         printEnumConstant(field);
9       }
10      writer.println("");
11      for (MethodDoc method : clss.methods(false)) {
12        printMethod(method);
13      }
14   }  else if (clss.isInterface()) {
15      for (ClassDoc subClass : subclasses(clss)) {
16         printSubClass(subClass);
17      }
18   }  else {
19      for (FieldDoc field : clss.fields(false)) {
20        printField(field);
21      }
22      for (MethodDoc method : clss.methods(false)) {
23        printMethod(method);
24      }
25   }
26 }

这个方法太大了,应该重构,但为了解释起见,我想将其全部显示在一页上,作为一个脚本。如您所见,此方法决定如何打印 Java/Doclet 元模型中每种元素(类、接口、子类、字段、方法、枚举、枚举常量)的活动术语表:

This method is too big and should be refactored, but for the sake of this explanation I wanted to show it all on one page, as a script. As you can see, this method decides how to print the living glossary for each kind of element in the Java/Doclet metamodel (class, interface, subclass, field, method, enum, enum constant):

1 私有 void printMethod(MethodDoc m) {
2 if (!m.isPublic() || !hasComment(m)) {
3返回;
4 }
5 最终字符串签名 = m.name() + m.flatSignature()
6 + ": " + m.returnType().simpleTypeName();
7 writer.println("- " + 签名 + " " + m.commentText());
8}
9
10
11
12 私有布尔 hasComment(ProgramElementDoc doc) {
13 返回 doc.commentText().trim().length() > 0;
14}
1  private void printMethod(MethodDoc m) {
2    if (!m.isPublic() || !hasComment(m)) {
3      return;
4    }
5    final String signature = m.name() + m.flatSignature()
6      + ": " + m.returnType().simpleTypeName();
7    writer.println("- " + signature + " " + m.commentText());
8  }
9
10
11
12 private boolean hasComment(ProgramElementDoc doc) {
13   return doc.commentText().trim().length() > 0;
14 }

你明白了。关键是要尽快让某些东西发挥作用,以便您可以获得有关术语表生成器(您的自定义 Doclet)和代码的反馈。然后就是迭代:您更改术语表生成器的代码以改进术语表的呈现并提高其选择性过滤的相关性,并且您更改项目的实际代码,以便通过添加注释和如果需要,创建新的注释,以便代码讲述整个业务领域知识。这个迭代周期不应该花费很多时间;然而,它永远不会真正结束,也没有结束状态,因为它是一个活生生的过程。术语表生成器或项目代码中总是有一些需要改进的地方。

You get the idea. The point is to have something working as soon as possible so you can get feedback on the glossary generator (your custom Doclet) and on the code as well. Then it’s all about iterating: You change the code of the glossary generator to improve the rendering of the glossary and to improve the relevance of its selective filtering, and you change the actual code of the project so that it is more expressive by adding annotations and creating new annotations if needed so that the code tells the whole business domain knowledge. This cycle of iterations should not take a lot of time; however, it never really finishes and does not have an end state because it’s a living process. There is always something to improve in the glossary generator or in the code of the project.

活生生的词汇表本身并不是目标。最重要的是,它是一个帮助团队反思其代码的过程,以便提高其质量。

A living glossary is not a goal in itself. It’s above all a process that helps a team reflect on its code so it can improve its quality.

生活图表

Living Diagrams

自动化应该使安全地更改代码变得更容易,而不是更困难。如果变得更难,请删除一些。并且永远不要将不断变化的东西自动化。

Automation should make it easier to change code safely, not harder. If it’s getting harder, delete some. And never automate stuff in flux.

——Liz Keogh (@lunivore) 在 Twitter 上

—Liz Keogh (@lunivore) on Twitter

有些问题很难用语言解释,但用图片解释就容易多了。这就是为什么我们在软件开发中经常使用图表来表示静态结构、动作序列和元素层次结构。

Some problems are difficult to explain with words but are much easier to explain with a picture. This is why we frequently use diagrams in software development for static structures, sequences of actions, and hierarchies of elements.

大多数时候,我们只需要对话期间的图表。餐巾纸上的快速草图非常适合这种情况。一旦解释了想法或做出了决定,您就不再需要图表了。

Most of the time we only need diagrams for the duration of a conversation. Quick sketches on a napkin are perfect for such a case. Once the idea has been explained or the decision made, you don’t need the diagram anymore.

但您可能想要保留一些图表,因为它们解释了每个人都应该知道的设计的重要部分。大多数团队创建图表并将其保存为单独的文档,例如幻灯片或 Visio 或 CASE 工具文档。

But you might want to keep some diagrams because they explain important parts of a design that everybody should know. Most teams create diagrams and keep them as separate documents, such as slides or Visio or CASE tools documents.

当然,问题是图表会变得过时。系统代码发生变化,没有人有时间或记得更新图表。因此,图表有点不正确是很常见的。人们已经习惯了这一点,并且学会了不要过于相信图表。这些图表变得越来越无用,直到有人有勇气删除它们。从那时起,将需要大量的技能来审视系统的本来面目,并尝试认识到它是如何设计的以及为什么设计。这变成了逆向工程的问题。

The problem, of course, is that a diagram will become outdated. The code of the system changes, and nobody has time or remembers to update the diagram. Consequently, it’s very common for diagrams to be a bit incorrect. People get used to this and learn not to trust diagrams too much. The diagrams become increasingly useless until someone has the courage to delete them. From that point, it will require a lot of skills to look at the system as it is and try to recognize how it was designed and why. It becomes a matter of reverse engineering.

这一切都令人沮丧,但最糟糕的是,重要的知识在这个过程中丢失了——而这些知识在一开始就存在。输入实时图表:您可以在更改后再次生成图表,以便它始终是最新的。

This is all frustrating, but the worst part is that important knowledge is lost in the process—and that knowledge was there at the beginning. Enter the living diagram: a diagram that you can generate again after a change so that it’s always up-to-date.

因此:每当图表长期有用时(例如,如果它已经使用过多次),您应该建立一种机制来自动从源代码生成图表,而无需任何手动操作。让您的持续集成在每个构建或单击按钮按需运行的特殊构建上触发它。不要每次都手动重新创建或更新图表。

Therefore: Whenever a diagram will be useful for the long term—for example, if it has already been used several times—you should set up a mechanism to automatically generate the diagram from the source code without any manual effort. Have your continuous integration trigger it on each build or on a special build that is run on demand at the click of a button. Don’t re-create or update the diagram manually each time.

图表有助于对话

Diagrams Assist in Conversations

拥有系统的动态图会产生意想不到的副作用:它使开发更加切实。你可以指出讨论中的事情。

Unexpected side effect of having a living diagram of the system: it makes development more tangible. You can point to things in discussions.

—推特上的@abdullin

—@abdullin on Twitter

对话和图表并不矛盾。始终能够参考反映软件当前状态的最新版本的图表是讨论的催化剂。

Conversations and diagrams are not incompatible. Always being able to refer to the latest version of a diagram that reflects the current state of the software is a catalyzer of discussions.

一张图,一个故事

One Diagram, One Story

当您手动创建和维护图表时,考虑到所花费的时间,您很容易将尽可能多的内容放在同一个图表上以节省精力,即使这样做对用户不利。然而,一旦图表自动化,就没有理由让它们变得更加复杂。创建另一个图表并不需要太多努力,因此您可以为已确定的受众的每个明确标识的目的创建另一个图表。

When you create and maintain diagrams manually, given the time it takes, it’s tempting to put as much as possible onto the same diagram to save effort, even if doing so is detrimental to its users. However, once diagrams are automated, there is no reason to make them more complicated. Creating another diagram is not so much effort, so you can create another diagram for each clearly identified purpose for an identified audience.

图表资源的供应有限,受众的时间和认知资源也是有限的,这是图表只能传达一种信息的主要原因。

Diagram real estate is in limited supply, and so are the time and cognitive resources of its audience, which are the main reasons a diagram should convey only one message.

旨在向非技术受众阐明系统的外部参与者的文档应该隐藏除系统之外的所有内容(作为黑匣子)以及每个参与者及其非技术名称以及与系统的业务关系。它不应显示有关 JBoss、HTTP 或 JSON 的任何内容。它不应显示组件或服务名称。这种选择性的视角决定了文档是否相关。试图同时展示不同内容的文档需要受众付出更多的努力,并且无法传达清晰的信息。

A document whose purpose is to clarify the external actors of a system for a nontechnical audience should hide everything except the system as a black box and each actor with its nontechnical name and the business relationship with the system. It should not show anything about JBoss, HTTP, or JSON. It should not show components or service names. This selective perspective is what makes a document relevant or not. A document that tries to show different things at the same time requires more work from its audience and does not convey a clear message.

如图6.7中有趣的海报所示,生动的图表一次应该只讲述一个故事。如果您想讲几个故事,请为每个故事制作一张图表。请记住:一张图表,一个故事。

As illustrated with the funny poster in Figure 6.7, a living diagram should tell only a single story at a time. If you want to tell several stories, make one diagram for each of them. Remember: one diagram, one story.

图中显示一个卡通人物说“我并不总是画活生生的图表,但当我这样做时,它们会讲述一个故事”。

图 6.7 一张图,一个故事

Figure 6.7 One diagram, one story

因此:请记住,每个图表都应该有一个且只有一个目的。抵制向现有图表添加额外信息的诱惑。相反,创建另一个图表,重点关注额外信息,并删除对于这个新的不同目的来说价值较低的其他信息。积极过滤多余信息;只有基本元素才值得将其放入图表中。

Therefore: Remember that each diagram should have one and only one purpose. Resist the temptation to add extra information to an existing diagram. Instead, create another diagram that focuses on the extra information and remove other information that is less valuable for this new different purpose. Filter superfluous information aggressively; only the essential elements deserve to make it onto a diagram.

一个相关的反模式是展示什么是方便的,而不是展示什么与已确定的目的相关。还记得 20 世纪 90 年代末的逆向工程/往返工具吗?一开始这很神奇,但最终我们都得到了如图6.8所示的图表(或更糟)。

A related anti-pattern is showing what’s convenient rather than showing what’s relevant to an identified purpose. Remember the reverse-engineering/round-trip tools of the end of the 1990s? It was magical at the beginning, but eventually we all ended up with diagrams like the one in Figure 6.8 (or worse).

一张图显示了许多彼此关联的 UML 图。

图 6.8 这个图有多大用处?

Figure 6.8 How useful is this diagram?

太多的信息就像没有信息一样:同样无用。图表需要经过大量严格的过滤才能发挥作用!但如果您清楚地了解图表的要点,那么您就已经成功了一半。

Too much information is like no information at all: It’s equally useless. It takes a lot of serious filtering for diagrams to be useful! But if you clearly know the point of a diagram, you’re already halfway there.

活图的一个挑战是从大量可用数据中过滤和提取相关数据。在任何现实世界的代码库中,未经过滤的活图几乎毫无用处;它只是一堆乱七八糟的盒子和电线,无法帮助任何人理解任何东西(参见图 6.8)。

A challenge with living diagrams is filtering and extracting only the relevant data out of the mass of available data. In any real-world code base, a living diagram without filtering is close to useless; it’s just a mess of boxes and wires that don’t help anyone understand anything (refer to Figure 6.8).

一张有用的图表可以说明一件事。它有一个明确的重点。它可能会显示依赖关系、层次结构或工作流程。或者它可能显示模块的特定分解或类之间的特定协作,如设计模式中那样。你说出它的名字,但你只选择一个。由于活文档是自动生成的,因此可以轻松地为您想要解释的每个方面创建一个图表;没有必要尝试混合它们。确定图表的焦点是编辑决定。

A useful diagram tells one thing. It has a clear focus. It might show dependencies or hierarchy or workflow. Or it might show a particular decomposition of modules or a particular collaboration between classes, as in a design pattern. You name it, but you choose only one. Because a living document is automatically generated, it’s easy to create one diagram for each aspect you want to explain; there’s no need to try to mix them. Determining the focus of a diagram is an editorial decision.

一旦选择了焦点,过滤步骤将仅选择真正对焦点有贡献的元素并忽略其余元素。理想情况下,此阶段最多应有七到九个元素。然后,对于每个元素,提取步骤提取仅与焦点真正相关的最小数据子集。你应该抵制展示一切的诱惑。如果您曾经尝试过具有神奇往返机制的 UML 工具,您就会看到可以得到多么复杂的图表。

Once the focus is chosen, the filtering step selects only the elements that really contribute to the focus and ignore the rest. Ideally there should be a maximum of seven to nine elements at this stage. Then, for each element, the extraction step extracts only the minimal subset of data that is really relevant for the focus. You should resist the temptation to show everything. If you’ve ever tried UML tools with magic round-trip mechanisms, you’ve seen what overly complex diagrams you can get.

生动的图表让你保持诚实

Living Diagrams to Keep You Honest

将活动图表的代码存储在源代码管理本身中非常重要。您希望一次又一次地运行它,以便当代码更改时,可以轻松生成更新的图表。该生成器甚至可以成为构建工具中的插件,在每次构建期间生成最新版本的图表。

It’s important to store the code for a living diagram in the source control itself. You want to run it again and again, so that when the code changes, it’s easy to generate an updated diagram. This generator could even become a plugin in the build tool that generates the latest versions of the diagrams during each build.

当活动图是构建的一部分时,它提供了另一种查看代码当前状态的方法。您可以在代码审查期间或设计会议期间查看它,或者只是随机查看一切是否符合预期。这种图表的最大好处是它按原样显示代码,这可能会让人大吃一惊。它让您对设计的质量保持诚实。正如我在 Twitter 上与 Rinat Abdullin 讨论的那样,如果您必须自己编写新模块,那么自动生成的图表可以是您的第一个开发反馈。如果您与同事一起工作,拥有系统的生动图表的另一个好处是,正如 Rinat 所说,“它使开发更加切实。你可以指出讨论中的事情。”

When a living diagram is part of a build, it provides another way to look at the current state of the code. You may have a look at it during code review, or during design meetings, or just randomly to see if everything is as expected. The biggest benefit from this kind of diagram is that it shows the code as it is, which can be a bad surprise. It keeps you honest about the quality of the design. As I discussed with Rinat Abdullin on Twitter, if you must code a new module on your own, an auto-generated diagram can be your first development feedback. And if you work with colleagues, another benefit of having a living diagram of the system is that, as Rinat said, “it makes development more tangible. You can point to things in discussions.”

追求完美图表

The Quest for the Perfect Diagram

从传统的手工制作的图表到完美的生活图表都有一定的规模,并且沿途的每个点都有不同程度的自动适应变化,并且需要不同程度的努力。比例越低,生成一张图表所需的工作量就越少,但根据变化更新图表所需的工作量就越大。图表的比例如下:

There is a scale from traditional, manually crafted diagrams to perfect living diagrams, and each point along the way has a different level of automatic adaptations to change and requires a different level of effort. The lower in the scale, the less effort is required to produce one diagram but the more effort is required to update the diagram in reaction to changes. Here’s what the scale of diagrams might look like:

  • 餐巾草图:这些一次性图表是用笔和纸创建的。这种类型的图表非常适合即时但一次性的。除了一支笔和一张随机的纸之外,不需要任何东西:信的背面、餐巾纸等等。

  • Napkin sketch: These throw-away diagrams are created using pen and paper. This type of diagram is perfect for the instant but disposable. There is no need for anything beyond a pen and a random piece of paper: the back of a letter, a napkin, whatever.

  • 专有图:这些图看起来不错,但需要花费大量时间来创建和维护。这不是首选选项,除非您想手动进行布局,如果您需要更完整的 UML 支持,如果您确实想要工具提供的所有额外功能,或者如果您必须依法使用这些类型的图表。它们非常耗时,并且只能由安装了该工具的人员进行编辑。它们很难区分,会产生大文件,并且需要时间来调整布局和每种图形可能性。

  • Proprietary diagram: These diagrams look nice but take a lot of time to create and maintain. This is not the preferred option unless you want to do the layout manually, if you need more complete UML support, if you really want all the extra features the tools offer, or if you have to use these types of diagrams by law. They are time-consuming and editable only by people with the tool installed. They are hard to diff, they produce large files, and it takes time to adjust the layout and every graphical possibility.

  • 纯文本图表:纯文本易于维护、源代码控制友好且差异友好。它支持查找和替换操作,但您仍然需要维护它。这些图表具有可塑性、易于更改且易于区分。一些 IDE 可以传播重构,例如重命名文本中的类名,这可能有助于减少维护,但这也会限制文本。ASCII 图是纯文本图的一种特殊形式。

  • Plain-text diagrams: Plain text is easy to maintain, source control friendly and diff friendly. It supports find and replace operations, but you still have to maintain it. These diagrams are malleable, easy to change, and easy to diff. Some IDEs can propagate refactorings such as renaming to class names in text, which may help reduce maintenance, but this can constrain the text, too. ASCII diagrams are a particular flavor of plain-text diagrams.

  • 代码驱动图:您可以使用代码而不是纯文本来创作图表。当类被重命名(并且图也被重命名)或删除(当编译器告诉您有问题时)时,它甚至可以是重构证明。这些图更具重构性,是具有专用代码和/或应用程序代码的编程图(例如,由包含代码引用的 DSL 驱动)。

  • Code-driven diagram: You may author a diagram by using code rather than plain text. It could even be refactoring proof when a class is renamed (and the diagram is renamed, too) or removed (when the compiler tells you there is something wrong). These diagrams, which are more refactoring proof, are programmatic diagrams with dedicated code and/or application code (for example, driven by a DSL that includes references to code).

  • 动态图:该图完全是从代码库或运行时的软件系统创建的(参见第 7 章“运行时文档”)。

  • Living diagram: The diagram is totally created from the code base or from the software system at runtime (see Chapter 7, “Runtime Documentation”).

如果您只需要一次图表,然后在使用后可以立即将其扔掉,请选择餐巾纸草图。另一方面,如果知识足够重要,需要在一段时间内使用,请从频谱中选择另一种风格的图表。选择一个你觉得舒服的。对于除了一些添加、删除和重命名重构之外不会发生太大变化的简单图表,我建议使用纯文本图表或代码驱动图表。

If you need a diagram only once and then you can throw it away immediately after use, choose a napkin sketch. On the other hand, if the knowledge is important enough to use over a period of time, choose another flavor of diagram from the spectrum. Choose one you feel comfortable with. For simple diagrams that won’t change much except for a few additions, deletions, and renaming refactorings, I recommend plain-text diagram or code-driven diagrams.

如果您需要一个漂亮的图表来说服或推销,那么生成的图表可能不太合适。生成的图表很少看起来特别有吸引力。一旦图表成为其本身的一部分,就值得把它做好,使用正确的工具让它闪闪发光。您可以尝试商业专有的 CASE 工具,但最终您将需要求助于图形设计工具,甚至调用图形设计师来完成这项工作。

If you need a beautiful diagram to convince or sell, then a generated diagram is probably not a good fit. Generated diagrams seldom look particularly attractive. As soon as the diagram becomes a stake in itself, it becomes worth doing it well, using the right tools to make it shiny. You may try commercial proprietary CASE tools, but you will eventually need to resort to graphical design tools or even calling a graphic designer to do the job.

图片哈哈

LOL

“我知道这个绘图工具不友好,你讨厌它,但你必须使用它,我们已经购买了无限企业许可证,并且有一个四人的支持团队来提供帮助!”

“I know this diagramming tool is not friendly and you hate it, but you must use it, we have already bought an unlimited enterprise license, and there’s a support team of four people to help!”

渲染生动的图表

Rendering a Living Diagram

使用编程语言创建图表的方法有很多种,并且这个主题可以填充许多其他书籍,适用于各种技术和各种上下文。本章甚至没有试图涵盖所有内容,而是旨在让您更好地了解该过程。

There are many possible ways to create diagrams using programming languages, and this topic could fill many other books, for various technologies and various contexts. This chapter doesn’t even try to cover them all but aims to give you a good idea of the process.

请记住,图表应该讲述一个故事。一个故事。它应该隐藏与故事无关的所有内容。因此,动态图表的大部分工作就是忽略所有不属于故事核心的内容。故事必须是图表的唯一焦点。

Remember that a diagram should tell a story. One story. It should hide everything that does not matter for the story. As a result, most of the work for a living diagram is in ignoring everything that is not central to the story. The story must be the sole focus of the diagram.

动态图的生成取决于您需要创建哪种类型的图,通常涉及四个步骤:

The generation of a living diagram depends on what kind of diagram you need to create, and it typically involves four steps:

  1. 扫描源代码。

  2. Scan the source code.

  3. 从大量元素中过滤出相关部分。

  4. Filter the relevant parts out of the huge number of elements.

  5. 从每个部分中提取相关信息,包括与图表重点相关的少数有意义的关系。

  6. Extract the relevant information from each part, including the few meaningful relationships that are relevant for the focus of the diagram.

  7. 使用与图表焦点匹配的布局呈现信息。

  8. Render the information using a layout that matches the focus of the diagram.

让我们看一个简单的例子。假设您有一个包含许多类的代码库,其中一些类与顺序的概念相关。您希望看到一个仅关注与订单相关的类以及它们如何相互依赖的图表。

Let’s look at a simple example. Say that you have a code base with many classes, some of which are related to the concept of order. You’d like to see a diagram that focuses only on the order-related classes and how they depend on each other.

代码库如下所示:

The code base looks like this:

1 ...
2 订单
3 订单谓词
5 简单订单
6 复合订单
7 订单工厂
8 订单
9 订单编号
10 下订单
11 取消订单
12 ... // 许多其他类
1  ...
2  Order
3  OrderPredicates
5  SimpleOrder
6  CompositeOrder
7  OrderFactory
8  Orders
9  OrderId
10 PlaceOrder
11 CancelOrder
12 ... // many other classes

首先,您需要一种扫描代码的方法。您可以为此使用反射或动态加载代码。从包开始,您可以枚举它的所有元素。

First, you need a way to scan the code. You can use reflection or dynamically loading code for that. Starting from a package, you can then enumerate all its elements.

该应用程序的领域模型中有很多类,因此您需要一种方法来过滤您感兴趣的元素。在这里,您对与顺序概念相关的每个类或接口感兴趣。为了简单起见,您可以对名称中包含“order”的所有元素进行过滤。

There are many classes in the domain model of this application, so you need a way to filter the elements you’re interested in. Here you’re interested in every class or interface related to the concept of order. For the sake of simplicity, you can do the filtering on all elements that contain “order” in their name.

现在您需要决定图表的焦点。在这种情况下,假设您想要显示类之间的依赖关系,也许是为了突出显示那些可能不需要的依赖关系。为此,在扫描所有类和接口期间,您将仅提取它们的名称以及它们之间的聚合依赖关系。例如,您可以收集构成类依赖项的所有字段类型、枚举常量、方法参数类型和返回类型以及超类型。通常,您可以通过使用 Java 语言的简单解析器以及遍历所有声明(导入、超类、实现的接口、字段、方法、方法参数、方法返回和异常)的访问者来完成此操作,并将找到的所有依赖项收集到一组中。您可能决定忽略其中一些。

Now you need to decide the focus of the diagram. In this case, say that you’d like to show dependencies between the classes, perhaps to highlight those that may be undesired. To do this, during the scan of all the classes and interfaces you will extract only their name and the aggregated dependencies between them. For example, you can collect all field types, enum constant, method parameters types and return types, and super types that constitute the dependencies of a class. You typically do this by using a simple parser for the Java language and with a visitor who walks through all declarations—imports, superclass, implemented interfaces, fields, methods, method parameters, method return, and exceptions—collecting all dependencies found into one set. You may decide to ignore some of them.

最后一步是使用专门的库渲染图表。如果使用Graphviz,则需要将具有依赖关系的类的模型转换为Graphviz文本语言。完成后,您运行该工具并获得图表。

The last step is to render the diagram, using a specialized library. If you use Graphviz, you need to convert the model of classes with dependencies into the Graphviz text language. When that is finished, you run the tool and get a diagram.

笔记

Note

在此示例中,对于名称包含 的每个类Order,您将拥有其名称及其依赖项列表。它已经是一个可以映射到任何图形渲染库(例如 Graphviz)的图形。

In this example, for each class with a name containing Order, you would have its name and its list of dependencies. It is already a graph that you can map to any graph rendering library, such as Graphviz.

有很多工具可用于渲染,但没有多少工具可以对任意图形进行智能布局。Graphviz 可能是最好的,但它是一个原生工具。幸运的是,它现在也作为 JavaScript 库存在,并且很容易包含到网页中以在浏览器中呈现图表。而这个JavaScript库也成为了一个纯Java库,graphviz-java!2我曾经在 Graphviz 点之上使用我的旧 Java 包装器点图3 ,但 graphviz-java 现在似乎是一个更好的选择。

There are many tools available for rendering, but not many of them can do a smart layout of an arbitrary graph. Graphviz is probably the best, but it’s a native tool. Fortunately, it now also exists as a JavaScript library and is easy to include into a web page to render a diagram in a browser. And this JavaScript library has also become a pure Java library, graphviz-java!2 I used to use my old little Java wrapper dot-diagram3 on top of Graphviz dot, but graphviz-java now seems like a better alternative.

2.graphviz -java, https://github.com/nidi3/graphviz-java

2.graphviz-java, https://github.com/nidi3/graphviz-java

3.点图, https://github.com/cyriux/dot-diagram

3.dot-diagram, https://github.com/cyriux/dot-diagram

关于工具的一句话

A Word on Tools

一些可以帮助渲染动态图表的工具和技术包括 Pandoc、D3.js、Neo4j、AsciiDoc、PlantUML、ditaa、Dexy 以及 GitHub 和 SourceForge 上的许多其他不太知名的工具。创建普通 SVG 文件也是一种选择,但您必须自己进行布局。但是,如果您也可以将其用作模板,就像使用模板创建动态 HTML 页面一样,那么这可能是一个好方法。Simon Brown 的 Structurizr 是另一个工具。

Some tools and technologies that can help render a living diagram include Pandoc, D3.js, Neo4j, AsciiDoc, PlantUML, ditaa, Dexy, and many other not-so-well-known tools on GitHub and SourceForge. Creating a plain SVG file is an option, too, but you have to do the layout yourself. However, it may be a good approach if you can use it as a template, too, as you would do dynamic HTML pages with a template. Simon Brown’s Structurizr is another tool.

要扫描源代码,您需要解析器。有些解析器只能解析元模型,而其他解析器可以访问代码注释。例如,在 Java 中,Javadoc 标准 Doclet 或 QDox 等替代工具使您可以访问结构化注释。另一方面,优秀的 Google Guava ClassPath 仅提供对编程语言元模型的访问,这在许多情况下就足够了。

To scan the source code, you need parsers. Some parsers can only parse the metamodel, while others have access to the code comments. For example, in Java, the Javadoc standard Doclet or alternative tools such as QDox give you access to the structured comments. On the other hand, the excellent Google Guava ClassPath only gives access to the programming language’s metamodel, which is enough in many cases.

让我们按布局复杂性来查看图表类型:

Let’s look at diagram types by layout complexity:

  • 表格(可能不是真正的图表,但它们有严格的布局)

  • Tables (which are perhaps not really diagrams, but they have a strict layout)

  • 固定在固定背景上,例如 Google 地图上的标记,它提供了一种方法来映射每个元素的 ( x , y ) 位置以固定在背景上

  • Pins on a fixed background, like the markers on Google Map, which provide a way to map an (x, y) location for each element to pin on the background

  • 使用源代码中的实际内容提取进行评估的图表模板(例如,SVG、DOT)

  • Diagram templates (for example, SVG, DOT) that are evaluated with the actual content extracts from the source code

  • 简单的一维流程图(从左到右或从上到下),这是您甚至可以自己编程的简单布局

  • Simple one-dimensional flow diagrams (left-to-right or top-to-bottom), which are simple layouts you could even program yourself

  • 管道、序列图和进出生态系统黑匣子

  • Pipelines, sequence diagrams, and in-out ecosystem black boxes

  • 树结构(从左到右、从上到下或放射状),可能很复杂,但如果您确实愿意,可以自己做。

  • Tree structures (left-to-right, top-to-bottom, or radial), which can be complicated but which you can do yourself if you really want to.

  • 继承树和层

  • Inheritance trees and layers

  • 遏制,涉及自动布局,例如使用 Graphviz 的集群功能

  • Containment, which involves auto layout, for example using the cluster feature of Graphviz

  • 布局丰富,垂直、水平布局、包容性

  • Rich layout, with vertical and horizontal layout as well as containment

当然,如果你想更有创意,你也可以尝试将图表变成一件艺术品,例如照片拼贴,甚至将其变成动画或互动的东西。

Of course, if you want to be more creative, you can also try to turn a diagram into a piece of art, such as a photo collage, or even turn it into something animated or interactive.

可视化指南

Visualization Guidelines

为什么这么多工程师认为复杂的系统图令人印象深刻?真正令人印象深刻的是解决难题的简单方法。

Why do so many engineers think complicated system diagrams are impressive? What’s truly impressive are simple solutions to hard problems.

——推特上的@nathanmarz

—@nathanmarz on Twitter

最终的经验法则:如果图表中至少有一条线与另一条线交叉,则系统太复杂。

The ultimate rule of thumb: if there is at least one line crossing another in a diagram, the system is too complicated.

—推特上的@pavlobaron

—@pavlobaron on Twitter

好的文档有一些规则,例如显示或列出不超过五到九个项目,并选择与问题结构一致的布局或列表样式或表格或图表。

There are rules for what makes a good document, such as showing or listing no more than five to nine items and choosing a layout or list style or a table or chart that is congruent with the structure of the problem.

为了充分利用图表,请考虑让一切都有意义:

To get the most from your diagrams, consider making everything meaningful:

  • 使左右轴和自上而下的轴有意义:示例可能包括从左到右的因果关系、左侧的 API 和右侧的 SPI,以及从上到下的依赖关系。

  • Make the left–right and top-down axes meaningful: Examples might include causality relations left-to-right, API on the left and SPI on the right, and dependencies top-to-bottom.

  • 使布局有意义:例如,元素之间的接近可能意味着“相似性”,而包含可能意味着“专业化”。

  • Make the layout meaningful: For example, proximity between elements could mean “similarity,” while containment could mean “specialization.”

  • 使大小和颜色有意义:例如,视觉元素的大小或颜色可以反映其重要性、严重性或其某些属性的大小。

  • Make the size and color meaningful: For example, the size or color of a visual element may reflect its importance, its severity, or the magnitude of some of its attributes.

示例:六边形架构生活图

Example: Hexagonal Architecture Living Diagram

六边形架构是分层架构的演变,并且在依赖约束方面更进一步。六角形结构只有两层:内部和外部。有一条规则:依赖关系必须从外部到内部,而不是相反。

The hexagonal architecture is an evolution of the layered architecture and goes further with respect to dependency constraints. The hexagonal architecture has only two layers: an inside and an outside. And there’s a rule: Dependencies must go from the outside to the inside and never the other way round.

如图 6.9所示,内部是领域模型,干净且没有任何技术损坏。外部是其余的,特别是使软件与世界其他地方相关的工作所需的所有基础设施。域位于中心,有时周围有一个小的应用程序层,通常显示在左侧。围绕域模型的是适配器,用于集成域模型和连接到世界其他地方的端口:数据库、中间件、servlet 或 REST 资源等。

As shown in Figure 6.9, the inside is the domain model, clean and free from any technical corruption. The outside is the rest, in particular all the infrastructure required to make the software work in relation to the rest of the world. The domain is in the center, sometimes with a small application layer around it usually shown on the left. Around the domain model are adapters to integrate the domain model and ports that connect to the rest of the world: databases, middleware, servlets or REST resources, and so on.

简而言之,该图显示了六边形架构。 显示了中心标记为“内部域”的六边形。 六边形外部绘制了三个指示。

图 6.9 六边形架构简述

Figure 6.9 Hexagonal architecture in a nutshell

假设您必须为遵循六边形架构的项目创建文档,可能是因为老板要求这样做,或者因为您希望能够向同事解释这种漂亮的架构。你是怎样做的?

Say that you have to create documentation for a project that follows the hexagonal architecture, perhaps because the boss asked for it or because you’d like to be able to explain this nice architecture to a colleague. How do you do that?

该架构已经记录在案

首先要认识到的是,这种架构已经在行业文献的许多地方进行了记录,首先是 Alistair Cockburn 的网站,他首先使用图 6.10 中所示的老式图表描述了这种模式

The first thing to realize is that this architecture is already documented in many places in the industry literature, starting with the website of Alistair Cockburn, who first described this pattern with the vintage diagram shown in Figure 6.10.

下图显示了 Alistair Cockburn 网站上的六边形架构图。

图 6.10 来自 Alistair Cockburn 网站的六边形架构图

Figure 6.10 Hexagonal architecture diagram from Alistair Cockburn’s website

许多书籍中也描述了这种架构模式,包括Steve Freeman 和 Nat Pryce 的《Growing Object-Oriented Software》、《Guided by Tests》(GOOS),以及Vaughn Vernon 的Implementing DDD (IDDD)。这种模式在 .Net 圈子里也被称为洋葱架构,由 Jeffrey Palermo 提出。

This architecture pattern is also described in many books, including Growing Object-Oriented Software, Guided by Tests by Steve Freeman and Nat Pryce (GOOS), and Implementing DDD by Vaughn Vernon (IDDD). This pattern is also known in the .Net circles as the onion architecture, proposed by Jeffrey Palermo.

因为关于六边形架构的信息太多了,所以没有必要你自己解释太多。您只需链接到已经解释得很好的外部参考即可。为什么要尝试重写别人已经写过的内容?这是现成的架构文档。

Because there is so much information about hexagonal architecture, there is no need for you to explain much about it yourself. You can just link to an external reference that already explains it well. Why try to rewrite what’s been already written by someone else? This is ready-made architecture documentation.

架构已经在代码中

该架构本身已经记录在文献中,但是它在您的自定义项目中的特定实现又如何呢?

The architecture itself is already documented in the literature, but what about its particular implementation in your custom project?

因为您对自己的技术很认真,所以六边形架构已经存在于代码中:域模型位于其自己的包中(分别是 .Net 中的命名空间或项目),并且基础设施位于一个或多个其他包中,这一点很明显与领域模型分离。

Because you’re serious about your craft, the hexagonal architecture is already there in the code: The domain model is in its own package (respectively, namespace or project in .Net), and the infrastructure is in one or several other packages, clearly segregated from the domain model.

有了对此模式的一些经验,您只需查看包及其内容就可以识别它。如此干净而严格的隔离绝非偶然发生。它展示了清晰的设计意图。如果你只看代码就能认出六边形架构,那么你就完成了,对吧?

With some experience with this pattern, you can recognize it just by looking at the packages and their content. Such clean and strict segregation never happens by pure chance; it demonstrates a clear design intent. If you can recognize the hexagonal architecture just by looking at the code, you’re done, right?

嗯,不是真的。并不是每个人都知道六角形建筑,但建筑是每个人都应该了解的。您需要以某种方式使架构变得明确。已经有 99% 了,但是您需要添加缺失的 1%,以使其对每个人都完全可见。您需要使用注释或命名约定来进行一些知识扩充,这两种方法在这里都可以很好地发挥作用。

Well, not really. Not everyone knows about hexagonal architecture, and architecture is something everybody should be aware of. You need to make the architecture explicit in some way. It’s 99% there already, but you need to add the missing 1% to make it fully visible to everyone. You need to do some knowledge augmentation, using annotations or naming conventions, both of which would work well here.

事实上,命名约定已经存在:

The naming convention is, in fact, already there:

  • 每个类、接口和枚举都位于根包下的包中*.domain.*

  • Every class, interface, and enum is in a package under the root package *.domain.*.

  • 每个基础设施代码都位于*.infra.*.

  • Every infrastructure code is under *.infra.*.

您需要将此约定记录下来,当然还要保持稳定。

You need this convention to be documented and, of course, stable.

您可以使用注释而不是命名约定。这将使您或其他人能够添加更多信息,例如理由:

You could use annotations instead of naming conventions. This would enable you or others to add more information, such as a rationale:

1 @HexagonalArchitecture.DomainModel(
2 基本原理=“保护域模型”,
3 个选项 = "DDD.Conformist")
4 包 flottio.fuelcardmonitoring.domain;
5
6 导入flottio.annotations.hexagonalarchitecture
7.六角形结构;
1  @HexagonalArchitecture.DomainModel(
2     rationale = "Protect the domain model",
3     alternatives = "DDD.Conformist")
4  package flottio.fuelcardmonitoring.domain;
5
6  import flottio.annotations.hexagonalarchitecture
7                               .HexagonalArchitecture;
了解您对活文档的需求

您可以通过在餐巾纸上涂鸦来开始弄清楚您的期望。这里您想要的是一个中心有六边形(或任何其他形状)的图表,表示域模型及其内部最重要的元素。在这个形状的外部和周围,您期望拥有基础设施的每个重要元素,并用箭头显示它们与内部域元素的依赖关系。它可能类似于图 6.11中的图表。

You can start figuring out what you expect by doodling on a napkin. What you want here is a diagram with a hexagon (or any other shape) at the center, representing the domain model with its most important elements inside. Outside and around this shape you expect to have every significant element of the infrastructure, with arrows showing their dependencies with the domain elements inside. It might look something like the diagram in Figure 6.11.

图中显示了您想要生成的图表类型的快速草图。

图 6.11 您想要生成的图表类型的快速草图

Figure 6.11 A quick sketch of the kind of diagram you’d like to generate

您需要一个从左到右的布局,从对 API 的调用到域,然后到服务提供者及其在基础设施中的实现。

You want a layout that flows from left-to-right, from the calls to the API to the domain, and then to the service providers and their implementations in the infrastructure.

现在知识在哪里?

正如您所看到的,有关六边形架构的大部分知识都存在于包所使用的命名约定中。其余的知识只是这些包中包含的每个类、接口和枚举的列表,以及它们之间的关系。

As you’ve seen, the bulk of the knowledge about the hexagonal architecture is in the naming convention used with the packages. The rest of the knowledge is simply the list of every class, interface, and enum contained in these packages, along with their relationships.

绘制六边形架构时的一个方便约定是将使用域模型的每个元素放在左侧,将向域模型提供服务的每个元素放在右侧。如何从源代码中提取这些信息?

A convenient convention when drawing hexagonal architecture is to have every element consuming the domain model on the left and every element providing services to the domain model on the right. How do you extract this information from the source code?

在当前的应用程序中,您有几个简化的机会:每个调用域模型的类都通过其成员字段来执行此操作,并且每个服务提供者都通过实现其接口之一来与域模型集成。这是一种常见的情况,但并不是规则;例如,调用者可能通过回调获取其响应。在其他情况下,如果您在图表布局中关心这一点,您可能必须明确声明谁在 API 端以及谁在 SPI(服务提供商)端。

In the current application you have several opportunities for simplification: Every class that calls the domain model does so through its member fields, and every service provider integrates with the domain model by implementing one of its interfaces. This is a common situation, but it is not a rule; for example, a caller may be getting its response through a callback. In other cases, you may have to declare explicitly who’s on the API side and who’s on the SPI (service provider) side if you care about that in the diagram layout.

过滤掉不相关的细节

即使在小型项目中,源代码也包含大量信息,因此您始终需要仔细决定将哪些内容排除在图表之外在这种情况下,您需要排除以下内容:

Even in small projects, the source code contains a lot of information, so you always need to carefully decide what to keep out of the diagram. In this case, you want to exclude the following:

  • 每个原始的

  • Every primitive

  • 每个充当原语的类(如最基本的值对象)

  • Every class that acts as a primitive (like the most basic value objects)

  • 与图中提到的其他类不相关的每个类

  • Every class that is not related to other classes mentioned in the diagram

您希望通过以下方式包含类:

You want to include classes in the following fashion:

  • 包括域模型中的所有类和接口(除了准基元,例如测量单位)。存在于域模型中是命名约定的问题,或者是存在于这样注释的包中的问题。

  • Include all classes and interfaces within the domain model (apart from quasi-primitives such as units of measurement). Being in the domain model is a matter of naming convention, or of being in a package annotated as such.

  • 包括有意义的相互关系。您可能希望将类型层次结构折叠到其超类型中以节省图表空间。

  • Include mutual relationships that make sense. You may want to fold type hierarchies into their supertype to save diagram real estate.

  • 包括与域模型中已包含的元素有关系的基础结构类。

  • Include infrastructure classes that have relationships with elements already included in the domain model.

  • 对于每个基础设施类,还包括其与域类的关系以及基础设施元素之间的关系。为了从左到右获得 API 到 SPI 方向的有向图,您需要帮助渲染器。例如,您需要确保生成的图表描述中的调用实现关系方向相反:A 调用 BA 实现 B 的方向必须相反。如果你现在不明白这一点,那也没有问题;一旦你尝试通过调整渲染使其工作,你就会清楚地理解它。

  • For each infrastructure class, include its relationship to the domain classes and between infrastructure elements, too. In order to have a directed diagram in the API-to-SPI direction from the left to the right, you need to help the renderer. For example you need to ensure that your call and implement relationships are in opposite directions in your generated diagram description: A calls B and A implements B must be in opposite directions. If you don’t understand this now, that is no problem; you will understand it clearly as soon as you try to make it work by tweaking your rendering.

所有这些只是在一种情况下运行良好的一个示例。它绝不是此类图表的通用解决方案。您应该尝试各种替代方案,如果您的图表变得太大,您可能必须更积极地进行过滤。例如,您可能决定根据附加注释仅显示核心概念。

All this is just one example that works fine in one context. It is by no mean a universal solution for this kind of diagram. You should expect to try various alternatives, and you may have to filter more aggressively if your diagram gets too big. For example, you may decide to show only the core concepts, based on additional annotations.

扫描源代码

对于像这样的活图,您所需要的只是一种迭代所有类的方法以及内省它们的能力。任何标准解析器都可以做到这一点,您甚至可以在没有任何解析器的情况下,仅通过使用反射来做到这一点。因为重点是六边形架构而不是其他,所以您的重点是隔离元素并突出它们之间的依赖关系。

For a living diagram like this one, all you need is a way to iterate through all the classes and the ability to introspect them. Any standard parser can do this, and you can even do it without any parser, just by using reflection. Because the focus is on the hexagonal architecture and nothing else, your focus is on segregating elements and highlighting the dependencies between them.

图6.12所示的示例使用标准的Java反射,借助Google Guava ClassPath可以方便地扫描完整的类路径。我自己的实用程序库 DotDiagram 是 Graphviz DOT 语法之上的一个便捷包装器,用于创建 .dot 文件。然后由 Graphviz dot 来进行自动神奇的布局和渲染。

The example shown in Figure 6.12 uses standard Java reflection, with the help of the Google Guava ClassPath to scan the full class path conveniently. My own utility library DotDiagram is a convenience wrapper on top of the Graphviz DOT syntax to create the .dot files. Then it’s up to Graphviz dot to do the auto-magical layout and rendering.

下图显示了从源代码生成的六边形架构生活图。

图6.12 源码生成的六边形架构生活图

Figure 6.12 Hexagonal architecture living diagram generated from source code

处理变更

假设您创建图 6.12所示的图表一个月后,您Trend对域接口的名称不满意,并决定重命名它SentencesAuditing。无需手动更新图表;下一次构建时会生成显示新名称的新的最新图表。

Say that a month after you create the diagram shown in Figure 6.12, you are not happy with a name Trend for the domain interface, and you decide to rename it SentencesAuditing. There is no need to update the diagram by hand; a new, up-to-date diagram showing the new name is generated on the next build.

可能的演变

六边形架构限制了依赖关系:它们只能从外到内,而不能从外到内。然而,动态图显示了所有依赖关系,甚至是那些违反规则的依赖关系。这对于让违规行为可见非常有用。

The hexagonal architecture constrains dependencies: They can only go from the outside to the inside and never the other way around. However, the living diagram shows all dependencies, even those that violate the rule. This is very useful for making violations visible.

还可以更进一步,用不同的颜色突出显示所有违规行为,例如使用红色大箭头来指示依赖关系的方向错误。生动的图表和执行指导方针的静态分析之间的界限非常细。

It’s possible to go even further and to highlight all violations in a different color, such as with big red arrows that indicate dependencies that are going the wrong direction. The line between a living diagram and static analysis to enforce guidelines is very thin.

您可能已经注意到,如果不深入讨论图表的用途,就不可能认真讨论生动的图表,换句话说,不讨论设计。这并非巧合。有用的图表必须是相关的,并且当您要描述设计意图时要具有相关性,您必须真正理解设计意图。这表明做好设计文档与做好设计是一致的。

You may have noticed that it’s impossible to talk seriously about a living diagram without talking deeply about the purpose of the diagram—in other words, without talking about design. This is no coincidence. Useful diagrams must be relevant, and to be relevant when you’re supposed to describe a design intent, you must really understand the design intent. This suggests that doing design documentation well converges with doing design well.

案例研究:活生生的业务概览

Case Study: A Business Overview as a Living Diagram

假设您在一家几年前推出的网上商店工作。该网上商店的软件系统是一个完整的电子商务系统,由多个成分。该系统必须处理在线销售所需的一切,从目录和导航到购物车、运输和一些基本的客户关系管理。

Say that you work for an online shop that was launched a few years ago. The software system for this online shop is a complete e-commerce system made of several components. This system has to deal with everything necessary for selling online, from the catalog and navigation to the shopping cart, the shipping, and some basic customer relationship management.

你很幸运,因为创始技术团队拥有良好的设计能力。结果,组件以一对一的方式匹配业务领域,如图6.13所示。换句话说,软件架构与其支持的业务非常一致。

You’re lucky because the founding technical team had good design skills. As a result, the components match the business domains in a one-to-one fashion, as illustrated in Figure 6.13. In other words, the software architecture is well aligned with the business it supports.

一张图说明了与业务领域一对一匹配的软件组件。

图6.13 软件组件与业务领域一一匹配

Figure 6.13 Software components match the business domains one-to-one

由于其成功,您的网上商店正在快速发展。因此,需要支持的新需求越来越多,这反过来意味着需要向组件添加更多功能。由于这种增长,您可能需要添加新组件、重做某些组件,以及将现有组件拆分或合并为更易于维护、发展和测试的新组件。

Because of its success, your online shop is growing quickly. As a result, there are an increasing number of new needs to support, which in turn means there are more features to add to the components. Because of this growth, you’ll probably have to add new components, redo some components, and split or merge existing components into new components that are easier to maintain, evolve, and test.

您还需要在开发团队中雇用新人员。作为新加入者必要的知识传播的一部分,您需要一些文档,首先概述系统支持的主要业务领域或领域。您可以手动创建它,在 PowerPoint 或某些专用图表工具中花费几个小时。但是您想要信任您的文档,并且您知道每当系统发生变化时您可能会忘记更新手动创建的文档,而且您知道它会发生变化。

You also need to hire new people in the development teams. As part of the necessary knowledge transmission for the new joiners, you want some documentation, starting with an overview of the main business areas, or domains, supported by the system. You could create it manually, spending a couple of hours in PowerPoint or in some dedicated diagramming tool. But you want to trust your documentation, and you know you’ll likely forget to update a manually created document whenever the system changes—and you know it will change.

幸运的是,在您阅读一本有关活文档的书后,您决定从源代码自动生成所需的图表。您不想花时间在手动布局上;基于域之间关系的布局将非常好——如图 6.14所示。

Fortunately, after you read a book on living documentation, you decided to automatically generate the desired diagrams from the source code. You don’t want to spend time on a manual layout; a layout based on the relationships between the domains will be perfectly fine—something like what is sketched in Figure 6.14.

下图显示了预期的图表样式。

图 6.14 预期的图表样式

Figure 6.14 Expected style of diagram

实际实现:现有源代码

您的系统由简单的 Java 包组件组成,如图6.15所示。

Your system is made of components that are simply Java packages, as shown in Figure 6.15.

该图显示了作为 Java 包的组件的概述。

图 6.15 作为 Java 包的组件概述

Figure 6.15 Overview of the components as Java packages

这些包的命名有点不一致,因为历史上这些组件是根据开发项目代码命名的,这种情况经常发生。例如,负责运输功能的代码被命名为“Fast Delivery Pro”,因为这是营销团队两年前为自动运输计划命名的名称。不过,除了作为包名称之外,该名称已不再使用。同样,Omega 实际上是负责目录和当前导航功能的组件。

The naming of these packages is a bit inconsistent because historically the components were named after the development project code, as it is often the case. For example, the code that takes care of the shipping features is named Fast Delivery Pro because that’s the name the marketing team gave the automated shipping initiative two years ago. This name is not used anymore, though, except as a package name. Similarly, Omega is actually the component that takes care of the catalog and the current navigation features.

您遇到的命名问题也是文档问题:代码没有告诉业务。由于某种原因,您现在无法重命名软件包,但您希望明年能够重命名。然而,即使有正确的名称,包也无法说明它们之间的关系。

You have a naming problem that is also a documentation problem: The code does not tell the business. For some reason you can’t rename the packages right now, although you hope to be able to do it next year. However, even with the right names, the packages won’t tell the relationships between them.

增强代码

由于当前代码中存在命名问题,您需要额外的信息才能制作有用的图表。正如您之前所见,添加的一种好方法知识编码就是使用注释。至少,您需要在代码中添加以下知识来修复命名:

As a result of the current naming problems in code, you need extra information in order to make a useful diagram. As you’ve seen before, one great way to add knowledge to code is to use annotations. At a minimum, you want to add the following knowledge to the code to fix the naming:

1 @BusinessDomain(“运输”)
2 org.livingdocumentation.sample.fastdeliverypro
3
4 @BusinessDomain(“目录和导航”)
5 org.livingdocumentation.sample.omega
1  @BusinessDomain("Shipping")
2  org.livingdocumentation.sample.fastdeliverypro
3
4  @BusinessDomain("Catalog & Navigation")
5  org.livingdocumentation.sample.omega

您引入一个仅带有名称的自定义注释来声明业务域:

You introduce a custom annotation with just a name to declare a business domain:

1 @Target({ ElementType.PACKAGE })
2 @Retention(RetentionPolicy.RUNTIME)
3 公共@interface BusinessDomain {
4 字符串值();// 域名
5 }
1  @Target({ ElementType.PACKAGE })
2  @Retention(RetentionPolicy.RUNTIME)
3  public @interface BusinessDomain {
4          String value(); // the domain name
5  }

现在您想要表达域之间的关系:

Now you’d like to express the relationships between the domains:

  • 目录商品在订购前会被放入购物车中。

  • The catalog items are placed into the shopping cart before they are ordered.

  • 然后订单中的商品必须发货。

  • Then the items in orders must be shipped.

  • 还对这些项目进行统计分析,以便为客户关系管理提供信息。

  • These items are also analyzed statistically to inform the customer relationship management.

然后,您可以使用相关域的列表来扩展注释。然而,一旦您多次引用同一个名称,文本名称就会产生一个小问题:如果您更改一个名称,那么您必须在提到它的所有地方进行更改。为了解决这个问题,您需要将每个名称分解到一个可供引用的位置。一种可能性是使用枚举类型而不是文本。然后您可以引用枚举类型的常量。如果重命名一个常量,则无需执行任何特殊操作即可在各处更新其引用。因为您还想讲述每个链接的故事,所以还为链接添加文本描述:

You then extend the annotation with a list of related domains. However, as soon as you refer to the same name several times, text names raise a little problem: If you change one name, then you must change it everywhere it is mentioned. To remedy this, you want to factor out each name into a single place to be referenced. One possibility is to use enumerated types instead of text. You can then make references to the constants of the enumerated type. If you rename one constant, you’ll have nothing special to do to update its references everywhere. Because you also want to tell the story for each link, you add a text description for the link as well:

1 公共@interface BusinessDomain {
2 域值();
3 String link() 默认 "";
4 Domain[] related() 默认 {};
5 }
6
7 // 声明每个域的枚举类型
8 // 地点
9 公共枚举域{
9   CATALOG(“目录和导航”),
10 购物(“购物车”),
11 运输(“运输”),CRM(“CRM”);
12
13 私有字符串值;
14
15 私有域(字符串值){
16 this.value = 值;
17}
18
19 公共字符串 getFullName() {
20 返回值;
21}
22}
1  public @interface BusinessDomain {
2          Domain value();
3          String link() default "";
4          Domain[] related() default {};
5  }
6
7 // The enumerated type that declares each domain in one
8 // place
9 public enum Domain {
9   CATALOG("Catalog & Navigation"),
10  SHOPPING("Shopping Cart"),
11  SHIPPING("Shipping"), CRM("CRM");
12
13       private String value;
14
15       private Domain(String value) {
16              this.value = value;
17       }
18
19       public String getFullName() {
20              return value;
21       }
22 }

现在只需使用每个包上的注释来显式添加代码中缺少的所有知识即可:

Now it’s just a matter of using the annotations on each package to explicitly add all the knowledge that was missing from the code:

1 @BusinessDomain(值 = Domain.CRM,
2 link =“过去的订单用于统计
3 客户关系管理分析》,
4 相关 = {Domain.SHOPPING}))
5 org.livingdocumentation.sample.crm
6
7 @BusinessDomain(值 = Domain.SHIPPING,
8 link =“订单中的商品已运送到
9 送货地址",
10 个相关 = {Domain.SHOPPING})
11 org.livingdocumentation.sample.fastdeliverypro
12
13 //等。
1  @BusinessDomain(value = Domain.CRM,
2          link = "Past orders are used in statistical
3          analysis for customer relationship management",
4          related = {Domain.SHOPPING}))
5  org.livingdocumentation.sample.crm
6
7  @BusinessDomain(value = Domain.SHIPPING,
8          link = "Items in orders are shipped to the
9          shipping address",
10         related = {Domain.SHOPPING})
11 org.livingdocumentation.sample.fastdeliverypro
12
13 //etc.
生成生活图

因为您需要一个在所有情况下都像魔术一样工作的全自动布局,所以您决定使用 Graphviz 工具来布局和渲染图表。此工具需要一个扩展名为 .dot 且符合 DOT 语法的文本文件。您需要在运行 Graphviz 之前创建此纯文本文件,以将其渲染为常规图像文件。

Because you need a fully automatic layout that works like magic in all cases, you decide to use the tool Graphviz for the layout and rendering of the diagram. This tool expects a text file with a .dot extension that conforms to the DOT syntax. You need to create this plain-text file before running Graphviz to render it into a regular image file.

生成过程包括以下步骤:

The generation process involves the following steps:

  1. 扫描源代码或类文件以收集带注释的包及其注释信息。

  2. Scan the source code or the class files to collect the annotated packages and their annotation information.

  3. 对于每个带注释的包,将一个条目添加到 DOT 文件中:

    • 添加代表模块本身的节点

    • 添加指向每个相关节点的链接

  4. For each annotated package, add an entry to the DOT file:

    • To add a node that represents the module itself

    • To add a link to each related node

  5. 保存点文件。

  6. Save the DOT file.

  7. 通过向其传递 .dot 文件名和生成图像文件所需的选项,在命令行运行 Graphviz dot。

  8. Run Graphviz dot at the command line by passing it the .dot filename and the desired options to generate an image file.

你完成了!图像已在磁盘上准备就绪。

You’re done! The image is ready on disk.

执行所有这些操作的代码可以放在一个少于 170 行代码的类中。因为您使用的是 Java,所以大部分代码都是关于处理文件的,而其中最困难的部分是扫描 Java 源代码。

The code to do all this can fit inside a single class of fewer than 170 lines of code. Because you’re in Java, most of this code is about dealing with files, and the hardest part of it is about scanning the Java source code.

运行 Graphviz 后,您将得到如图 6.16所示的活动图。

After running Graphviz, you get the living diagram shown in Figure 6.16.

下图显示了从源代码生成的实际图表。

图6.16 源码生成的实际图

Figure 6.16 Actual diagram generated from the source code

添加一些额外的样式信息后,您将得到如图 6.17所示的图表。

After adding some additional style information, you get the diagram shown in Figure 6.17.

下图显示了从源代码生成的实际图表,带有样式。

图 6.17 从源代码生成的实际图,带样式

Figure 6.17 Actual diagram generated from the source code, with style

适应变化

一段时间后,业务增长了,支撑的软件系统也要增长。出现了一些新组件——一些是全新的,一些是拆分现有组件的结果。例如,现在您拥有用于以下业务领域的专用组件:

After some time, the business has grown, and the supporting software system has to grow as well. Several new components have appeared—some brand new and some as a result of splitting existing components. For example, now you have dedicated components for the following business domains:

  • 搜索和导航

  • Search & Navigation

  • 计费

  • Billing

  • 会计

  • Accounting

每个新组件都有自己的包,并且必须在其包注释中声明其知识,就像任何行为良好的组件一样。然后,无需任何额外的努力,您的生活图表将自动适应并生成新的、更复杂的概览图,如图6.18所示。

Each new component has its own package and has to declare its knowledge in its package annotation, like any well-behaved component. Then, without any additional effort, your living diagram will automatically adapt and produce the new, more complicated overview diagram shown in Figure 6.18.

下图显示了一段时间后从源代码生成的新图表。

图 6.18 一段时间后从源代码生成的新图

Figure 6.18 The new diagram generated from the source code, some time later

添加其他信息

现在您想通过质量属性等问题来丰富图表。由于代码中缺少这些知识,因此您需要通过扩充代码来添加它。您可以再次使用包注释来实现此目的,如图6.19所示。

Now you’d like to enrich the diagram with concerns such as quality attributes. Because this knowledge is missing from the code, you need to add it by augmenting the code. You can once again use package annotations for that, as shown in Figure 6.19.

下图显示了 package-info.java 中的 Package 注释。

图6.19 package-info.java中的包注释

Figure 6.19 Package annotations in package-info.java

您现在可以增强活动图表处理器以提取信息@Concern并将其包含在图表中。完成此操作后,您将得到如图 6.20所示的图,该图显然比之前的图稍微不太清晰。

You can now enhance the living diagram processor to extract the @Concern information as well to include it in the diagram. After you do this, you get the diagram shown in Figure 6.20, which is obviously a little less clear than the previous diagrams.

该图显示了从源代码生成的实际图表,以及附加的质量属性。

图 6.20 从源代码生成的实际图表,带有附加质量属性

Figure 6.20 Actual diagram generated from the source code, with additional quality attributes

本案例研究提供了一个示例,展示了动态图表的可能性。主要限制是你的想象力和尝试想法所需的时间,其中一些可能行不通。然而,时不时地或者每当对文档或设计感到沮丧时,花时间尝试一下想法是值得的。活文档使您的代码、其设计和架构对每个人都透明可见。如果您不喜欢所看到的内容,则需要在源代码中修复它。

This case study provides an example of what’s possible with a living diagram. The main limits are your imagination and the time required to try ideas, some of which may not work. However, it’s worth the time to try playing with ideas from time to time or whenever there’s frustration about the documentation or about the design. Living documentation makes your code, its design, and its architecture transparent for everyone to see. If you don’t like what you see, you need to fix it in the source code.

生活图表如何适应活文档的模式?

该图是一个活文档,只要系统发生变化,它就会自动刷新。如果您要添加或删除模块,图表将与下一个构建一样快地调整。

This diagram is a living document that is automatically refreshed whenever the system changes. If you were to add or delete a module, the diagram would adjust as quickly as the next build.

本案例研究提供了一个图表示例,该图表通过显示简要说明的链接讲述从一个节点到下一个节点的故事。

This case study provides an example of one diagram that tells a story from one node to the next through links that display brief descriptions.

该图是增强代码的示例,使用注释通过其相应业务领域的知识来增强每个主模块。这也是跨多个包进行信息整合的情况。

This diagram is an example of augmented code, using annotations to augment each main module with the knowledge of its corresponding business domain. This is also a case of information consolidation spread across many packages.

最后,添加到源代码中的知识可用于关于架构的强制指南。编写验证器与编写活动图生成器类似,只不过节点之间的关系用作依赖项白名单来检测意外的依赖项而不是生成图。

Finally, the knowledge added to the source code can be used for an enforced guidelines about architecture. Writing a verifier is similar to writing a living diagram generator except that the relationships between nodes are used as a dependency whitelist to detect unexpected dependencies instead of generating a diagram.

示例:上下文图

Example: A Context Diagram

没有一个系统是一座孤岛;每个系统都是一个更大的生态系统的一部分,其中包括其他参与者,通常是人和其他系统。从开发人员的角度来看,与其他系统的集成有时被认为是显而易见的知识,不值得记录,特别是在系统的早期。但一段时间后,系统不断发展并与许多其他参与者深度集成,甚至团队中的人也不再了解这个生态系统。要重新构建整个图景,您必须手动检查所有代码并采访知识渊博的人(他们也恰好很忙)。

No system is an island; every system is part of a bigger ecosystem with other actors, typically people and other systems. From a developer’s point of view, integration with other systems is sometimes considered obvious knowledge not worth documenting, especially in the early years of a system. But after a while the system grows and becomes deeply integrated with many other actors, and even people on the team no longer know about this ecosystem. To reconstitute the whole picture, you have to review all the code manually and interview knowledgeable people (who also happen to be very busy).

在考虑本系统或另一个外部系统的变化时,背景知识对于推理对其他参与者或来自其他参与者的影响至关重要。因此,它应该随时清晰可见并保持最新状态。基本上,上下文图提供了使用系统(API 端)或系统(服务提供商端)使用的所有参与者的回顾:

Context knowledge is essential for reasoning about impacts to or from other actors when considering changes in this system or in another external system. As such, it deserves to be made clearly visible and up-to-date at any time. Basically, a context diagram provides a recap of all actors using the system (API side) or used by the system (service providers side):

1 使用 * 的演员 --> 系统 --> * 使用的演员
2.由系统使用系统
1  Actors using * --> System --> * Actors used
2  using the system              by the system

上下文可以表示为一个简单的列表,如下所示:

The context can be expressed as a simple list, like this:

  • API(使用系统的参与者)

    • Fuelo 卡 API

    • 车队管理团队

    • 支持与监控

  • API (actors using the system)

    • Fuelo Card API

    • Fleet Management Teams

    • Support & Monitoring

  • SPI(向系统提供服务的参与者)

    • 谷歌地理编码

    • Garmin 的 GPS 追踪

    • 旧车辆分配

  • SPI (actors providing services to the system)

    • Google Geocoding

    • GPS Tracking from Garmin

    • Legacy Vehicle Assignment

但视觉布局也有优点,如图6.21所示。

But a visual layout has advantages too, as shown in Figure 6.21.

该图显示了生成的上下文图,左侧有三个参与者,右侧有三个参与者。

图 6.21 生成的上下文图,左侧三个参与者,右侧三个参与者

Figure 6.21 A generated context diagram with three actors on the left and three actors on the right

您可以在每次需要时手动创建这样的图表,并根据手头的问题进行定制。或者你可以生成它。

You can create such a diagram by hand each time you need it, tailoring it to the matter at hand. Or you could generate it.

图 6.21所示的图表是从本书示例中使用的示例 Flottio 车队管理系统生成的。该图通过与外部参与者的链接讲述了系统的故事,并对其中一些参与者进行了一些简要描述。

The diagram shown in Figure 6.21 was generated from the sample Flottio fleet management system used in examples throughout this book. This diagram tells the story of the system through its links to external actors, with some brief descriptions on some of them.

笔记

Note

上下文图这个名称借用自 Simon Brown 的 C4 模型,4是一种轻量级的架构图方法,在开发人员中越来越流行。

The name context diagram is borrowed from Simon Brown’s C4 model,4 a lightweight approach to architecture diagrams that is becoming increasingly popular among developers.

4. Simon Brown,编码架构博客, http://www.codingthearchitecture.com/2014/08/24/c4_model_poster.html

4.Simon Brown, Coding the Architecture blog, http://www.codingthearchitecture.com/2014/08/24/c4_model_poster.html

该图是一个活文档,只要系统发生变化,它就会自动刷新。它是通过扫描增强源代码并调用图形布局引擎(例如 Graphviz)生成的。如果您要添加或删除模块,图表将与下一个构建一样快地调整。该图也是一个防重构图的示例;如果您想重命名代码中的模块,图表也会显示它已重命名,无需额外的努力。无需每次都启动 PowerPoint 或图表编辑器。

This diagram is a living document that is automatically refreshed whenever the system changes. It is generated by scanning the augmented source code and calling a graph layout engine such as Graphviz. If you were to add or delete a module, the diagram would adjust as quickly as the next build. This diagram is also an example of a refactoring-proof diagram; if you want to rename a module in the code, the diagram will show it renamed, too, without extra effort. There is no need to fire up PowerPoint or a diagram editor each time.

相应源代码位置的超链接

您的实时文档可以包含指向代码库中准确位置的超链接。通过这些链接,用户可以单击图表上的任何外部参与者,在线跳转到源代码存储库中的相应 URL。(为此,您可以使用第 8 章“可重构文档”中的稳定链接模式之一

Your living document can feature hyperlinks to the accurate locations in the code base. With these links, a user can click on any external actor on the diagram to jump to the corresponding URL in the source code repository online. (For this you can use one of the patterns for stable links from Chapter 8, “Refactorable Documentation.”)

请注意,即使没有链接,也可以逐字使用图中的措辞在代码库中执行搜索。因为这个写法来自于代码,所以很容易找到对应的位置。

Note that even without a link, the wording in the diagram can be used verbatim to perform a search in the code base. Because the wording came from the code, it would be easy to find the corresponding location.

应用增强代码和知识整合

当然,问题是自动识别外部参与者及其名称、描述和使用方向(使用或正在使用)。不幸的是,我还没有找到奇迹般的解决方案。

The problem, of course, is to identify automatically the external actors and their names, descriptions, and directions of use (using or being used). Unfortunately, I haven’t found a miracle solution for that.

要生成此图,必须使用一些注释来增强代码以声明外部 actor。这是增强代码的示例,也是跨多个包和子包传播的信息合并的情况。

To generate this diagram, the code has to be augmented with some annotations to declare the external actor. This is an example of augmented code and is also a case of consolidation of information spread across multiple packages and subpackages.

例如,该软件包flottio.fuelcardmonitoring.legacy负责与遗留系统集成,以将车辆分配给驾驶员,驾驶员是所考虑系统的服务提供商:

For example, the package flottio.fuelcardmonitoring.legacy takes care of the integration with the legacy system for vehicle assignments to drivers, a provider of services for the system under consideration:

1 /**
2 * 车辆管理是一个遗留系统,用于管理哪些车辆
3 * 驾驶员与车辆关联一段时间。
3 */
4
5 @ExternalActor(
6 name =“旧版车辆分配系统”,
7 类型=系统,
8 方向=ExternalActor.Direction.SPI)
9 包 flottio.fuelcardmonitoring.legacy;
10
11 导入静态flottio.annotations.ExternalActor
12 .ActorType.SYSTEM;
13 导入 flottio.annotations.ExternalActor;
1  /**
2  * Vehicle Management is a legacy system which manages which
3  * drivers is associated to a vehicle for a period of time.
3  */
4
5  @ExternalActor(
6    name = "Legacy Vehicle Assignment System",
7    type = SYSTEM,
8    direction = ExternalActor.Direction.SPI)
9  package flottio.fuelcardmonitoring.legacy;
10
11 import static flottio.annotations.ExternalActor
12                                       .ActorType.SYSTEM;
13 import flottio.annotations.ExternalActor;

另一个例子是监听传入消息总线的类,它基本上使用系统来检查加油卡交易是否有异常:

Another example is the class listening to the incoming message bus, which basically uses the system to check whether fuel card transactions have anomalies:

1 个 flottio.fuelcardmonitoring.infra 包;
2 // 更多导入...
3
4 /**
5 * 监听传入的加油卡交易
6 * 加油卡提供商的外部系统
7 */
8 @ExternalActor(
9      name =“Fuelo 加油卡提供商”,
10 类型=系统,
11 方向=Direction.API)
12 公共类FuelCardTxListener {
13 //...
1 package flottio.fuelcardmonitoring.infra;
2 // more imports...
3
4 /**
5 * Listens to incoming fuel card transactions from the
6 * external system of the Fuel Card Provider
7 */
8 @ExternalActor(
9      name = "Fuelo Fuel Card Provider",
10     type = SYSTEM,
11     direction  = Direction.API)
12 public class FuelCardTxListener {
13  //...

您不必使用注释。您还可以在与注释代码相同的文件夹中添加 sidecar 文件,其内容与内部注释相同,作为 YAML、JSON 或 .ini 文件:

You don’t have to use annotations. You could also add sidecar files in the same folder as the annotation code, with the same content as the annotation inside, as a YAML, JSON, or .ini file:

1 ; 外部端口.ini
2 ; 此 sidecar 文件位于集成代码文件夹中
3 name=Fuelo 加油卡提供商
4 类型=系统
5方向=API
1  ; external-port.ini
2  ; this sidecar file is in the integration code folder
3  name=Fuelo Fuel Card Provider
4  type=SYSTEM
5  direction=API

假设在某个时刻,您想要向上下文图添加信息,因此您可以将此信息添加到集成代码的 Javadoc 中的代码本身,然后图就会更新,如图 6.22所示

Say that at some point, you want to add information to the context diagram, so you add this information to the code itself, in the Javadoc of the integration code, and then the diagram gets updated as shown in Figure 6.22.

该图显示了更新后的生成上下文图,左侧有三个参与者,右侧有三个参与者。

图 6.22 生成的上下文图,左侧三个参与者,右侧三个参与者

Figure 6.22 A generated context diagram with three actors on the left and three actors on the right

该生命系统图的局限性和优点

由于需要使用注释文件来扩充一些代码,因此存在不了解某些外部参与者的风险。

Because of the need for some code augmentation with annotation files, there is a risk of not knowing about some external actors.

如果在您的项目中您只能枚举几种执行集成的方法,则您可以尝试检测所有这些方法并将它们添加到图表中,除非通过代码增强显式静音。

If in your project you can enumerate only a few ways to perform integration, you may try to detect them all and add them to the diagram unless silenced explicitly through code augmentation.

无论如何,通过数据库进行的集成将很难检测和记录。您可能认为该数据库是您系统的私有细节,但如果另一个系统直接查询或写入它,那么在不与罪魁祸首对话的情况下将很难找到答案。

In any case, integration through the database will be hard to detect and document. You may believe the database is a private detail of your system, but if another system queries or writes into it directly, it will be hard to find out without a conversation with the culprits.

另一方面,该图显示了每个潜在的集成,但无法判断这些集成在生产中是否处于活动状态。如果代码库是产品线的工具包,它将显示所有潜在的集成,而不仅仅是特定实例中实际使用的集成。

On the other hand, this diagram shows every potential integration, but it cannot tell whether the integrations are active in production. If the code base is a toolkit for a product line, it will show all the potential integrations—not just the one actually used in practice in a particular instance.

与临时手动图相比,如图6.22所示的生成图的另一个缺点是它不是针对当前的特定问题而定制的。然而,生成图表比绘制临时图表要快得多。

Another drawback of a generated diagram like the one shown in Figure 6.22 compared to an ad hoc manual diagram is that it is not tailored for the particular matter at hand. However, it’s much faster to generate a diagram than to draw an ad hoc one.

尽管如此,您可能仍想调整图表生成器,例如,专注于上下文的子集。

Still, you may want to tweak the diagram generator—for example, to focus on a subset of the context.

自动生成设计文档的挑战

The Challenges with Automated Generation of Design Documentation

手动生成软件项目设计的文档需要大量工作,并且在下一次更改或重构后很快就会过时。手动绘制有意义的 UML 图非常耗时,甚至选择要显示的内容也需要花费大量时间。

Producing documentation of the design of a software project manually requires a lot of work and becomes obsolete very quickly after the next change or refactoring. Manually drawing meaningful UML diagrams is very time-consuming, and even choosing what to display takes a lot of time.

根据领域驱动设计,代码本身就是模型,但代码缺乏清晰表达大于类的结构和协作的能力。因此,一些额外的精心挑选的设计文档对于展示更大的图景很有用。它可以从代码生成,只要用设计意图对代码进行扩充即可。

According to domain-driven design, the code is itself the model, but code lacks the ability to clearly express larger-than-class structures and collaborations. Therefore, some additional carefully selected design documentation is useful to show the bigger picture. And it can be generated from the code, as long as the code is augmented with the design intentions.

在生成设计文档中使用模式

使用模式来帮助生成设计文档的过程是有前途的。模式自然地位于语言元素的“之上”。他们在特定背景下解决特定问题,讨论解决方案,并有明确的意图。它们涉及语言中多个元素的协作,例如多个类及其协议,或者只是类中字段和方法之间的关系。每个模式都是一大块设计知识。当需要自动化设计描述时,似乎也很自然地按模式对自动化过程进行分块。

The use of patterns to help with the process of generating design documentation is promising. Patterns naturally lie “on top” of the language elements. They address a particular problem within a context, discuss a solution, and have a clear intent. They involve a collaboration of several elements from the language, such as several classes and their protocols, or just relationships between fields and methods within a class. Each pattern is a chunk of design knowledge. When it comes the time to automate the description of the design, it seems natural to chunk the automation process by pattern as well.

在一些项目中,我声明了代码中使用的一些模式(使用注释),并创建了一些小工具来围绕这些模式派生软件设计的部分宏结构。每个模式都有一个上下文,而这个上下文帮助选择要显示的内容以及如何显示。根据代码中声明的模式,该工具可以生成比通用设计文档更好的设计文档(例如图表),该文档由按模式划分的知识块提供。

In some projects, I’ve declared some of the patterns used in the code (using annotations) and created little tools to derive partial macro structures of the software design around these patterns. Each pattern come with a context, and this context helps in selecting what to show and how to show it. From the patterns declared in the code, the tool can then generate better-than-generic design documentation (for example, a diagram) informed by the knowledge chunked pattern by pattern.

到目前为止,本书中的所有动态图示例都是在编译时从源代码生成的,但情况并非必须如此;也可以利用运行时知识来生成它们。

So far, all the examples of living diagrams in this book have been generated from the source code at compile time, but this doesn’t have to be the case; it’s also possible to exploit runtime knowledge to produce them.

概括

Summary

制作活文档非常有趣,有助于弥合快节奏项目与传统文档之间的差距,在某些情况下,传统文档可能仍然是可取的。本章中描述的设计图、术语表、概述图和特定领域图说明了如何使用自动化来生成智能文档。

Living documents are great fun to produce and help bridge the gap between fast-paced projects and traditional documents that may still be desirable in some cases. The design diagrams, glossary, overview diagrams, and domain-specific diagrams described in this chapter illustrate the use of automation to produce smart documents.

但重要的不仅仅是自动化图表;它还包括自动化图表。从已知来源获取图表也很重要,这样当图表发生变化时,文档也会发生变化。如本章所示,您需要稍微扩充源代码,例如,通过注释或任何其他扩充代码的方式显式声明要使用的模式或设计构造型,以实现可靠的自动化。

But it isn’t just automating diagrams that is important; deriving diagrams from known sources is also important so that when a diagram changes, the documentation does as well. As shown in this chapter, you need to augment the source code a little—for example, by declaring explicitly the patterns or design stereotype that you want to use through annotation or any other means of augmenting the code—to achieve reliable automation.

第7章

Chapter 7

运行时文档

Runtime Documentation

敏捷宣言呼吁“工作软件胜过全面文档”。

The Agile Manifesto calls for “Working Software over Comprehensive Documentation.”

如果工作软件本身就是一种文档怎么办?

What if the working software were itself a kind of documentation?

设计用户体验使得用户无需打开用户手册就可以与应用程序成功交互,这已经很常见了。然而,设计软件以便开发人员甚至无需打开源代码就可以理解它的情况并不常见。

It is already quite common to design the user experience so that the users can have successful interactions with an application without ever having to open the user manual. However, it’s less common to design software so that its developers can understand it without even having to open the source code.

只需使用相关且设计良好的应用程序就可以学习业务领域。该软件本身就是一份关于其自身及其业务领域的文档。这就是为什么应用程序的所有开发人员至少应该知道如何在大多数标准用例中使用他们的应用程序,即使它是一个处理复杂想法(例如金融工具)的复杂应用程序。

It is possible to learn the business domain just by using a related and well-designed application. The software is by itself a piece of documentation about itself and its business domain. This is why all the developers on an application should at least know how to use their application for most standard use cases, even if it is a complicated application that deals with complicated ideas (such as financial instruments).

关键

Key Point

任何可以回答问题的东西都可以被视为文档。如果您可以使用应用程序回答问题,那么该应用程序就是文档的一部分。

Anything that can answer a question can be considered documentation. If you can answer questions by using an application, then the application is part of the documentation.

第 6 章“自动化文档”中,您看到了几个基于源代码的活动图示例,但活动图也可以根据运行时可用的知识构建。让我们通过一个基于分布式跟踪的示例来了解这一点,该示例通常用于具有多个组件的分布式系统。

In Chapter 6, “Automating Documentation,” you saw several examples of living diagrams based on source code, but living diagrams can also be built from knowledge available at runtime. Let’s look at this by using an example based on distributed tracing, which is typically used on distributed systems with multiple components.

示例:生活服务图

Example: Living Services Diagram

基于 Google 的 Dapper 论文1的分布式跟踪正在成为微服务架构的重要组成部分。它是“分布式服务的新调试器”,是用于监视的关键运行时工具,通常用于解决响应时间问题。但它也是一个出色的现成的实时图表工具,可以在某一天发现整个系统及其所有服务的实时架构。

Distributed tracing, based on Google’s Dapper paper,1 is becoming a vital ingredient of a microservices architecture. It’s “the new debugger for distributed services,” a key runtime tool for monitoring, typically to solve response time issues. But it’s also a fantastic ready-made living diagram tool that can discover the living architecture of your overall system, with all its services, on a given day.

1. http://research.google.com/pubs/pub36356.html

1.http://research.google.com/pubs/pub36356.html

例如,Zipkin 和 Zipkin 依赖项提供了开箱即用的服务依赖关系图,如图7.1所示。此视图只不过是一段时间内(例如一天)每个分布式跟踪的聚合。

For example, the Zipkin and Zipkin Dependencies provide a services dependency diagram out-of-the-box, as shown in Figure 7.1. This view is nothing more than the aggregation of every distributed trace over some period (for example, for a day).

该图显示了屏幕上的 Zipkin 依赖关系图。

图 7.1 屏幕上的 Zipkin 依赖关系图

Figure 7.1 Zipkin Dependencies diagram on screen

增强代码的问题,但在运行时

A Matter of Augmented Code but at Runtime

为了使分布式跟踪发挥作用,您需要通过检测来增强系统。每个服务或组件都必须使用符合跟踪标识符的跟踪器来声明请求的接收、响应的发送以及注释以及作为键/值存储的附加“行李”。

For distributed tracing to work, you need to augment the system through instrumentation. Every service or component must use a tracer that conforms to the trace identifiers to declare the reception of a request, and the sending of the response, along with annotations, and additional “baggage” as a key/value store.

跟踪标识符涉及由三个标识符组成的上下文,使您能够将调用树构建为离线进程:

The trace identifiers involve a context made of three identifiers that enable you to build the call tree as an offline process:

  • Trace ID:完整调用树的相关ID

  • Trace ID: The correlation ID of a complete call tree

  • Span ID:此单个客户端/服务器调用的相关 ID

  • Span ID: The correlation ID of this single client/server call

  • 家长ID:当前家长呼叫

  • Parent ID: The current parent call

例如,可以使用 Spring Cloud Sleuth 使用注释来指定跨度名称:

The span name can be specified, for example, with Spring Cloud Sleuth, using an annotation:

1 @SpanName("计算税")
1  @SpanName("calculateTax")

用于定义客户端/服务器请求的开始和停止的一些核心注释如下:

Some of the core annotations used to define the start and stop of a client/server request are as follows:

  • cs:客户端启动

  • cs: Client start

  • sr:服务器接收

  • sr: Server receive

  • ss:服务器发送

  • ss: Server send

  • cr:客户收到

  • cr: Client receive

注释可以扩展以对您的服务进行分类或执行过滤。但是,这些工具可能并不自然支持您自己的注释。

The annotations may be extended to classify your services or to perform filtering. However, the tools may not naturally support your own annotations.

Baggage(或“二进制注释”)更进一步,捕获关键的运行时信息:

The baggage, or “binary annotation,” goes further, capturing key runtime information:

1 个响应代码 = 500
2 缓存.somekey = HIT
3 sql.query = "从用户中选择*"
4 特征标志.someflag = FALSE
5 http.uri = /api/v1/login
6 读写=只读
7 mvc.controller.class = 应用程序
8 测试 = 假
1  responsecode = 500
2  cache.somekey = HIT
3  sql.query = "select * from users"
4  featureflag.someflag = FALSE
5  http.uri = /api/v1/login
6  readwrite = READONLY
7  mvc.controller.class = Application
8  test = FALSE

在这里,所有元数据和其他实时数据的标记都是实时发生的。您可能会意识到这种方法类似于增强代码。您需要为工具注入一些知识才能提供更多帮助,而这种增强是在运行时发生的。

Here, all the tagging with metadata and other live data happens in real time. You might recognize that this approach is similar to augmented code. You need to inject some knowledge for tools to help more, and this augmentation happens at runtime.

发现架构

Discovering the Architecture

实时检查分布式系统的能力不仅仅适合前端开发人员。正如 Mick Semb Wever 在他的博客中所写的那样,将一段时间内的跟踪信息聚合到运行时服务依赖图中“对于帮助架构师和管理层准确理解事物的工作原理大有帮助,从而消除了对更高级别文档的大部分需求。” 2

The ability to inspect a distributed system in real time isn’t just for front-end developers. As Mick Semb Wever writes on his blog, aggregating traces over time into a runtime service dependency graph “goes a long way to help architects and management to understand accurately how things work, negating much of the need for higher level documentation.”2

2. Mick Semb Wever,《最后的泡菜》博客, http://thelastpickle.com/blog/2015/12/07/using-zipkin-for-full-stack-tracing-include-cassandra.html

2.Mick Semb Wever, The Last Pickle blog, http://thelastpickle.com/blog/2015/12/07/using-zipkin-for-full-stack-tracing-including-cassandra.html

使这一切发挥作用的魔力

The Magic That Makes This Work

通过采样,一些请求在经过系统的每个节点时都会被检测到。仪器生成跨度跟踪,这些跟踪被收集并存储在某个中央(逻辑)数据存储中。然后可以搜索并显示单独的痕迹。然后,每日cron触发器将所有跟踪后处理为表示服务之间“依赖关系”的聚合。聚合类似于这个简化的示例:

Through sampling, some requests get instrumented as they go through each node of the system. The instrumentation generates span traces that are collected and stored in some central (logically) datastore. Individual traces can then be searched and displayed. A daily cron triggers then post-processes all the traces into aggregates representing the “dependencies” between services. The aggregation is something like this simplified example:

1 选择不同的跨度
2 来自 zipkin_spans
3 其中开始和结束之间的span.start_timestamp
4、span.annotation in ('ca', 'cs', 'sr', 'sa')
按跨度5组
1  select distinct span
2  from zipkin_spans
3  where span.start_timestamp between start and end
4  and span.annotation in ('ca', 'cs', 'sr', 'sa')
5  group by span

然后,UI 使用某种自动化节点布局显示所有依赖项。

The UI then displays all the dependencies using some sort of automated nodes layout.

更进一步

Going Further

通过在标签上发挥创意,并通过测试机器人在预定义的场景中刺激系统,像 Zipkin 这样的分布式基础设施在生动的架构图方面具有很大的潜力:

By getting creative on the tags and through test robots stimulating the system on predefined scenarios, a distributed infrastructure like Zipkin has a lot of potential for living architecture diagrams:

  • 您可以从驱动一项或多项服务的测试机器人创建“受控”跟踪,并使用特定标签来标记相应的跟踪。

  • You can create “controlled” traces from a test robot driving one or more service(s), with a specific tag to flag the corresponding traces.

  • 您可以为“Cache = HIT”和“cache = MISS”场景显示不同的图表。

  • You can display different diagrams for the “Cache = HIT” and the “cache = MISS” scenarios.

  • 您可以显示整个系统中整个对话的“写入部分”与“读取部分”的不同图表。

  • You can display distinct diagrams for the “Write part” versus the “Read part” of an overall conversation across the system.

在这个领域尝试一些东西?请告诉我!

Trying something in this area? Please let me know!

可见的工作:工作软件作为自己的文档

Visible Workings: Working Software as Its Own Documentation

与软件作为文档相关的另一个想法是依靠软件本身来解释它的内部工作原理,布莱恩·马里克(Brian Marick)将其称为可见工作原理,涉及使内部机制从外部可见。3方法有很多种为了实现这一点,它们的共同点都是依靠软件本身来输出所需形式的文档。

Another idea related to software as documentation is to rely on the software itself to explain how it works inside, which Brian Marick calls visible workings and which involves making internal mechanisms visible from the outside.3 There are many ways to achieve this, and they all have in common relying on the software itself to output the desired form of documentation.

3. Brian Marick,“可见工作”: https://web.archive.org/web/20110202132102/ http://visibleworkings.com/

3.Brian Marick, “Visible Workings”: https://web.archive.org/web/20110202132102/http://visibleworkings.com/

例如,许多应用程序执行工资单或银行对账单的计算或其他形式的数据处理。通常有必要向业务人员或合规审计员等外部受众描述如何进行处理。

For example, many applications perform calculations for payroll or bank statements or other forms of data crunching. It is often necessary to describe how the processing is done for external audiences such as business people or compliance auditors.

您可能会将可见的工作方法视为一种导出或报告功能,向最终用户解释其内部工作方式。您希望能够询问软件“您如何计算它?” 或“这个结果的公式是什么?” 并让它在运行时告诉你答案。无需询问开发人员即可获得答案。

You may think of visible workings approaches as an exporting or reporting feature that explains to end users the way it works internally. You want to be able to ask the software “How do you compute that?” or “What’s the formula for this result?” and have it just tell you the answer at runtime. There should be no need to ask a developer to get the answer.

客户通常不会要求可见的工作,但它们是对更多文档需求的有效答案。可见的工作技术显然对于开发团队非常有用。这样的团队应该有充分的自由度来决定添加功能以使自己的生活更轻松,因为它显然是其项目的关键利益相关者之一。关键是花足够的时间来达到预期的效果。

Visible workings are not often requested by customers, but they’re a valid answer to a need for more documentation. Visible workings techniques are obviously very useful for development teams. Such a team should have full latitude to decide to add features to make its own life easier, since it’s obviously one of the key stakeholders of its project. The key is to spend just enough time for the expected benefit.

可见的测试

Visible Tests

好的测试始终根据预定义的断言检查代码。除非发生意外情况,例如断言失败或错误,否则它们会保持沉默。然而,我发现测试有时也可用于生成可见的输出,例如各种特定于域的符号的图表。

Good tests check the code against predefined assertions all the time. They are silent unless something unexpected occurs, such as a failed assertion or an error. However, I’ve found that tests can sometimes also be used to produce visible output such as diagrams in various domain-specific notations.

当以探索模式开始时,例如在峰值期间,当问题不清楚且不确定如何解决它们时,很难定义准确的断言。然而,可见的输出可以快速反馈其是否按预期工作。稍后,随着测试变成非回归工具,您可以添加实际的断言,但您可能仍然决定保留一些可见的输出作为显示正在发生的情况的方式。

When starting in exploration mode, such as during a spike, when the problems are not clear and you’re not sure how to solve them, it’s hard to define accurate assertions. However, visible output provides fast feedback on whether it works as expected or not. Later, as the tests turn into non-regression tools, you can add actual assertions, but you may still decide to keep some of the visible outputs as a way to show what’s happening.

特定领域的表示法

Domain-Specific Notation

随着时间的推移,许多业务领域都发展出了自己的特定符号。领域专家对符号很熟悉,通常用笔和纸来完成。

Many business domains have grown their own specific notations over time. Domain experts are comfortable with notation, usually doing it with pen and paper.

例如,对于供应链,我们倾向于从上游生产商到下游分销商绘制树,如图7.2所示。

For example, for a supply chain, we tend to draw trees from the upstream producers to the distributors downstream, as shown in Figure 7.2.

下图展示了供应链树。

图 7.2 供应链树

Figure 7.2 Supply chain tree

对于证券交易所来说,当需要决定如何进行匹配时,我们经常需要绘制订单簿,如图7.3所示。

For a stock exchange, we often have to draw order books when it is time to decide how the matching happens, as shown in Figure 7.3.

显示了代表匹配订单的订单簿的表格。

图7.3 撮合订单的订单簿

Figure 7.3 Order book for matching orders

在金融领域,金融工具在时间线上支付和接收现金流(金额),我们在时间线上使用垂直箭头绘制,如图7.4所示。

In finance, financial instruments pay and receive cash flows (amounts of money) over a timeline, which we draw using vertical arrows on a timeline, as shown in Figure 7.4.

下图显示了一段时间内的现金流量。

图 7.4 一段时间内的现金流量

Figure 7.4 Cash flows over a timeline

生成自定义的特定领域图表以获得视觉反馈

Generating Custom Domain-Specific Diagrams to Get Visual Feedback

很久以前,在一个新项目开始时,我曾经创建简单的测试,没有断言,只是生成基本的、丑陋的 SVG 文件,如图 7.5所示

Long ago, at the beginning of a new project I used to create simple tests with no assertions that simply generated basic, ugly SVG files like the one shown in Figure 7.5.

该图说明了生成 SVG 文件。

图 7.5 生成 SVG 文件

Figure 7.5 Generating an SVG file

将图 7.5中显示的信息与下面的电子表格进行比较:

Compare the information shown in Figure 7.5 to the following spreadsheet table:

1

1

13469欧元

EUR13469

20/06/2010

20/06/2010

2

2

13161欧元

EUR13161

2010年9月20日

20/09/2010

3

3

12715欧元

EUR12715

2010年12月20日

20/12/2010

4

4

12280欧元

EUR12280

20/03/2011

20/03/2011

5

5

12247欧元

EUR12247

20/06/2011

20/06/2011

6

6

11939欧元

EUR11939

20/09/2011

20/09/2011

7

7

11507欧元

EUR11507

20/12/2011

20/12/2011

8

8

11205欧元

EUR11205

20/03/2012

20/03/2012

9

9

11021欧元

EUR11021

2012年6月20日

20/06/2012

10

10

8266欧元

EUR8266

2012年9月20日

20/09/2012

11

11

5450欧元

EUR5450

2012年12月20日

20/12/2012

12

12

2695欧元

EUR2695

2013年3月20日

20/03/2013

使用图表可以更容易地直观地检查支付金额随时间的变化。

It’s much easier to check the evolution of the amounts paid over time visually, using the diagram.

当然,您也可以转储 CSV 文件并将其绘制在您最喜欢的电子表格应用程序中。或者您甚至可以通过编程方式生成一个包含图表的 XLS 文件;例如,在 Java 中,您可以使用 Apache POI 来执行此操作。

Of course, you could also dump a CSV file and graph it in your favorite spreadsheet application. Or you could even generate an XLS file with the graph inside programmatically; in Java, for example, you could use Apache POI to do this.

图 7.6显示了一个更复杂的生成图示例,它显示了现金流量如何受市场因素影响。

Figure 7.6 shows a more complicated example of a generated diagram, which shows how the cash flows are conditioned by market factors.

下图说明了为更复杂的金融工具的现金流量生成 SVG 文件。

图 7.6 为更复杂的金融工具的现金流量生成 SVG 文件

Figure 7.6 Generating the SVG file for the cash flows of a more complicated financial instrument

正如您所看到的,我不是 SVG 专家,这里的图只是快速图表,用于在较大项目的初始高峰期间获得视觉反馈。您可以使用现代 JavaScript 库来生成漂亮的图表!

As you can see, I’m not an expert with SVG, and the figures here are just quick graphs to get visual feedback during the initial spike of a bigger project. You could use a modern JavaScript library to produce beautiful diagrams!

对小黄瓜场景的补充?

A Complement to Gherkin Scenarios?

我还没有尝试过,但我希望 Cucumber 或 SpecFlow 中的一些关键场景除了断言的测试结果之外还能生成此类特定于域的图表。这听起来很可行,所以如果你碰巧尝试一下,请告诉我!

I haven’t tried it yet, but I’d love to have some key scenarios in Cucumber or SpecFlow produce such domain-specific diagrams in addition to the test results for their assertions. This sounds quite feasible, so if you happen to try it, please let me know!

示例:使用事件溯源时的可见测试

Example: A Visible Test When Using Event Sourcing

事件源是一种将应用程序状态的所有更改捕获为事件序列的方法。在这种方法中,应用程序状态的每次更改(领域驱动设计术语中的聚合)都由持久的事件表示。当前时间点的状态可以通过应用所有过去的事件来构建。

Event sourcing is a way to capture all changes to an application state as a sequence of events. In this approach, every change to the state of an application (an aggregate in domain-driven design terminology) is represented by events that are persisted. The state at a current point in time can be built by applying all past events.

当用户或系统的另一部分想要更改状态时,它通过命令处理程序向相应的状态持有者(聚合)发送命令。该命令可以被接受或拒绝。无论哪种情况,都会向每个感兴趣的人发送一个或多个事件。事件以过去时态的动词命名,只使用领域词汇。命令以祈使动词命名,也来自领域语言。

When a user or another part of the system wants to change the state, it sends a command to the corresponding state holder (the aggregate) through a command handler. The command can be accepted or rejected. In either case, one or more events are sent for everyone who is interested. Events are named as verbs in the past tense, using nothing but domain vocabulary. Commands are named with imperative verbs, also from the domain language.

我们可以用以下方式表示这一切:

We can represent all this in the following way:

1 鉴于过去的事件
2 当我们处理命令时
3 然后发出新事件
1  Given past events
2  when we handle a command
3  Then new events are emitted

在这种方法中,每个测试都是预期业务行为的一个场景,无需做太多工作即可使其成为用流利的英语进行业务可读的场景。所以我们又回到了典型的 BDD 优点——没有 Cucumber!

In this approach, each test is a scenario of the expected business behavior, and there is not much to do to make it a business-readable scenario in fluent English. So we are back to typical BDD goodness—without Cucumber!

因此:当您进行事件溯源时,您不需要“BDD 框架”。在这种方法中,如果命令和事件以领域语言正确命名,那么测试自然是业务可读的场景。如果您想要为非开发人员提供额外的报告,您可以通过事件溯源测试框架中的简单文本转换来打印事件和命令。

Therefore: You need no “BDD framework” when you’re doing event sourcing. In this approach, if the commands and events are named properly after the domain language, the tests are naturally business-readable scenarios. If you want additional reporting for nondevelopers, you can print the events and the command through simple text transformations in your event sourcing testing framework.

使用事件源有很多好处,其中之一是您几乎免费获得非常不错的自动化测试和实时文档。这最初是由 Greg Young 在各种演讲中提出的,4并且 Greg 已在 Github 上提供了他的相关 Simple.Testing 框架。5这个想法后来由 Jeremie Chassaing 详细阐述。6

There are many benefits of using event sourcing, and one of them is that you get very decent automated tests and living documentation almost for free. This was initially proposed by Greg Young in various talks,4 and Greg has made his related Simple.Testing framework available on Github.5 This idea was later elaborated by Jeremie Chassaing.6

4. Greg Young,《技能很重要》, http://skillsmatter.com/podcast/design-architecture/talk-from-greg-young

4.Greg Young, Skills Matter, http://skillsmatter.com/podcast/design-architecture/talk-from-greg-young

5.Simple.Testing https://github.com/gregoryyoung/Simple.Testing

5.Simple.Testing, https://github.com/gregoryyoung/Simple.Testing

6.杰里米·查萨因, https://twitter.com/thinkb4coding

6.Jeremie Chassaing, https://twitter.com/thinkb4coding

代码中的具体示例

A Concrete Example in Code

让我们考虑一个制作(和食用)批量饼干的示例,该示例取自 CQRS 邮件列表7上讨论 Greg 方法的 Brian Donahue:

Let’s consider an example of making (and eating) batches of cookies, taken from Brian Donahue on the CQRS mailing list7 discussing Greg’s approach:

7.布莱恩·多纳休, https://groups.google.com/forum/#! topic/dddcqrs/JArlssrEXIY

7.Brian Donahue, https://groups.google.com/forum/#!topic/dddcqrs/JArlssrEXIY

给定:使用 20 个 Cookie 创建批次

Given: Batch Created with 20 Cookies

时间:吃饼干:数量 = 10

When: Eat Cookies: Amount = 10

那么:吃掉的饼干:吃掉的数量= 10,剩余的数量:10

Then: Cookies Eaten: Amount Eaten = 10, AmountRemaining: 10

为了便于说明,我用 Java 创建了一个类似的非常简单的框架。8

For illustration purpose, I’ve created a similar very simple framework in Java.8

8.jSimpleTesting https://github.com/cyriux/jSimpleTesting

8.jSimpleTesting, https://github.com/cyriux/jSimpleTesting

在这种方法中,并使用此框架,通过直接使用形成事件源 API 的域事件和命令,场景按字面意思编写:

In this approach, and using this framework, the scenario is written literally in code, through the direct use of domain events and commands that form the event sourcing API:

1 @测试
2 公共无效 eatHalfOfTheCookies() {
3 场景(“吃掉该批次 20 块饼干中的 10 块”)
4.给定(新的BatchCreatedWithCookiesEvent(20))
5 .When(new EatCookiesCommand(10))
6 .Then(new CookiesEatenEvent(10, 10));
7}
1  @Test
2  public void eatHalfOfTheCookies() {
3    scenario("Eat 10 of the 20 cookies of the batch")
4      .Given(new BatchCreatedWithCookiesEvent(20))
5      .When(new EatCookiesCommand(10))
6      .Then(new CookiesEatenEvent(10, 10));
7  }

这是一个测试,“then”子句是一个断言。如果没有CookiesEatenEvent发出任何事件,则此测试失败。但这不仅仅是一个测试;它是一个测试。它也是实时文档的一部分,因为运行测试还以一种非常可读的方式描述了相应的业务行为,即使对于非开发人员来说也是如此:

This is a test, and the “then” clause is an assertion. If no CookiesEatenEvent event is emitted, then this test fails. But it’s more than just a test; it’s also a part of the living documentation, since running the test also describes the corresponding business behavior in a way that is quite readable, even for nondevelopers:

1 吃掉该批次 20 块饼干中的 10 块
2 个给定批次使用 20 个 Cookie 创建
3 何时吃饼干 10 块饼干
4 然后吃掉 10 块饼干,剩下 10 块饼干
1  Eat 10 of the 20 cookies of the batch
2         Given Batch Created With 20 Cookies
3         When Eat Cookies 10 cookies
4         Then 10 Cookies Eaten and 10 remaining cookies

这里框架只是调用并打印toString()测试中涉及的每个事件和命令的方法(也称为场景)。就是这么简单。

Here the framework just invokes and prints the toString() method of each event and command involved in the test (aka scenario). It is as simple as this.

因此,这不像 Cucumber 或 SpecFlow 等工具中手写的文本场景那样美观和“自然语言”,但也不错。

As a result, this is not as beautiful and “natural language” as text scenarios written by hand in a tool like Cucumber or SpecFlow, but it is not bad.

当然,聚合的先前历史记录中可以存在多个事件,并且应用命令的结果可以发出多个事件:

Of course, there can be more than one event in the prior history of the aggregate, and more than one event can be emitted as a result of applying the command:

1 @测试
2 公共无效 notEnoughCookiesLeft() {
3 场景(“只吃请求的 15 块饼干中的 12 块”)
4.给定(
5 新增 BatchCreatedWithCookiesEvent(20),
6 个新的 CookiesEatenEvent(8, 12))
7 .When(new EatCookiesCommand(15))
8.那么(
9 新 CookiesEatenEvent(12, 0),
10 个新的 CookiesWereMissingEvent(3));
11}
1  @Test
2  public void notEnoughCookiesLeft() {
3     scenario("Eat only 12 of the 15 cookies requested")
4       .Given(
5          new BatchCreatedWithCookiesEvent(20),
6          new CookiesEatenEvent(8, 12))
7       .When(new EatCookiesCommand(15))
8       .Then(
9          new CookiesEatenEvent(12, 0),
10         new CookiesWereMissingEvent(3));
11 }

第二种情况将打印为以下文本:

This second scenario would print as the following text:

1 只吃 15 块饼干中的 12 块
2 个给定批次使用 20 个 Cookie 创建
吃掉 3 块和 8 块饼干,剩下 12 块饼干
4 何时吃饼干 15 块饼干
5 然后吃掉 12 块饼干,剩下 0 块饼干
6 块和 3 块饼干不见了(不再有饼干了)
1  Eat only 12 of the 15 cookies requested
2         Given Batch Created With 20 Cookies
3         And 8 Cookies Eaten and 12 remaining cookies
4         When Eat Cookies 15 cookies
5         Then 12 Cookies Eaten and 0 remaining cookies
6         And 3 Cookies were missing (no more cookies)

这个小框架只是一个使用、、 和三个方法之间的方法链接来生成测试用例的构建器。每个方法都存储作为参数传递的事件和命令。最后调用该方法会运行完整的测试,并通过调用每个事件和命令的方法打印其文本场景,前缀为关键字、或。当关键字重复时,它会被别名为。Given(Event...)When(Command)Then(Event...)Then()toString()GivenWhenThenAnd

This little framework is just a builder producing test cases using method chaining between the three methods Given(Event...), When(Command), and Then(Event...). Each method stores the events and command passed as parameters. Calling the Then() method at the end runs the full test and prints its text scenario by calling the toString() method of each event and command, prefixed with the keyword Given, When, or Then. When a keyword is repeated, it is aliased by And.

该方法按照您希望打印和记录的方式实例化框架的类。从这里开始,您可以进一步详细说明,而不仅仅是测试。例如,您还可以使用这些测试中的知识将可能的行为记录为活图。scenario(title)SimpleTest

The method scenario(title) instantiates the SimpleTest class of the framework the way you want it to print and log. From there, you can elaborate to go further than just tests. For example, you might also use the knowledge from these tests to document the possible behaviors as living diagrams.

事件溯源场景的生动图表

Living Diagrams from Event Sourcing Scenarios

在上一节所示的示例中,测试检查行为并以任何人都可读的纯文本形式打印业务行为的描述。

In the example shown in the previous section, the test checks the behavior and prints a description of the business behavior in plain text that is readable by anyone.

有多个测试,每个测试都有不同的传入事件、命令和输出事件。所有这些测试的联合代表了所考虑聚合的用例。这通常就足够了。

There are several tests, each with different incoming events, commands, and out-coming events. The union of all these tests represents the use cases for the considered aggregate. This is often enough.

如果您想将此测试转换为图表,基于事件源的测试框架可以收集测试套件中的所有输入和输出,以便打印传入命令和传出事件的图表。

If you want to turn this test into diagrams, the event sourcing–based testing framework can collect all these inputs and outputs across the test suite in order to print a diagram of the incoming commands and the outgoing events.

每个测试都会收集命令和事件。测试套件完成后,就可以按以下方式打印图表了:

Each test collects commands and events. When the test suite has completed, it’s time to print the diagram in the following fashion:

1 添加聚合作为图的中心节点
2 将每个命令添加为一个节点
3 将每个事件添加为一个节点
4
5 将每个命令的链接添加到聚合中
6 添加从聚合到每个命令的链接
1  add the aggregate as the central node of the diagram
2  add each command as a node
3  add each event as a node
4
5  add a link from each command to the aggregate
6  add a link from the aggregate to each command

当它在浏览器中使用 Graphviz 渲染时,您会得到如图 7.7所示的内容。

When this is rendered with Graphviz in the browser, you get something like what is shown in Figure 7.7.

该图显示了 cookie 库存聚合的命令、聚合和事件的生成动态图。

图 7.7 生成的 cookie 库存聚合的命令、聚合和事件的实时图表

Figure 7.7 The generated living diagram of commands, aggregate, and events for the cookies inventory aggregate

您可能会发现这种图表有用,也可能不会,您可以根据这种方法制作自己的图表。这个例子说明自动化测试是一个数据源,可以挖掘有价值的知识,然后将其转化为活文档或动态图表。

You may or may not find this kind of diagram useful, and you can make your own based on this approach. This example illustrates that automated tests are a data source to be mined for valuable knowledge that can then be turned into a living document or a living diagram.

请注意,图 7.7中所示的相同内容也可以呈现为表格:

Note that the same content shown in Figure 7.7 could also be rendered as a table:

Cookie 库存命令

Cookies Inventory Commands

BakeCookiesCommand

BakeCookiesCommand

EatCookiesCommand

EatCookiesCommand

Cookie 库存事件

Cookies Inventory Events

BatchCreatedWithCookiesEvent

BatchCreatedWithCookiesEvent

CookiesEatenEvent

CookiesEatenEvent

CookiesWereMissingEvent

CookiesWereMissingEvent

您可能还希望避免将场景混合在一起,或者您可能决定向同一张图片添加更多信息。例如,您可以删除EventCommand后缀的噪音。您可以根据您的特定情况定制此想法。

You might also want to avoid mixing scenarios together, or you might decide to add more information to the same picture. You might, for example, remove the noise of the Event or Command suffixes. You can customize this idea for in your particular context.

自省的工作原理:内存中的代码作为知识的来源

Introspectable Workings: Code in Memory as a Source of Knowledge

在运行时,代码通常采用对象树的形式,即通过使用新运算符、工厂或构建器或依赖注入框架(例如 Spring 或 Unity)创建的对象树。

At runtime, code often takes the form of an object tree, a tree of objects that you create by using new operators, factories, or builders or dependency injection frameworks such as Spring or Unity.

通常,对象树的确切性质取决于配置,甚至取决于请求,如图7.8所示。

Often, the exact nature of the object tree depends on the configuration or even on a by-request basis, as illustrated in Figure 7.8.

图形表示运行时对象树根据配置或请求的变化。

图 7.8 运行时的对象树可能会根据配置或请求而变化

Figure 7.8 An object tree at runtime may vary depending on the configuration or the request

您如何知道给定请求的对象树在运行时到底是什么样子?常规方法是查看源代码并尝试想象它将如何连接对象树。但您可能仍然想检查您的理解是否正确。

How do you know what the object tree really looks like at runtime for a given request? The regular way is to look at the source code and try to imagine how it will wire the object tree. But you would probably still like to check whether your understanding is correct.

因此:在运行时内省对象树,以显示对象的实际排列、对象类型及其实际结构。

Therefore: Introspect trees of objects at runtime in order to display the actual arrangement of objects, their object types, and their actual structure.

在 Java 或 C# 等语言中,这可以通过反射或通过要内省的结构的每个成员上的方法来完成,如图7.9所示。这个想法最简单的形式是依靠toString()每个元素的方法来通过某种缩进方案来讲述它自己和它自己的成员。当使用依赖注入(DI)容器时,您不妨尝试要求容器告知它构造了什么。

In languages like Java or C#, this can be done through reflection or through methods on each member of the structure to be introspected, as shown in Figure 7.9. The simplest form of this idea is to rely on the toString() method of each element to tell about itself and about its own members with some indentation scheme. When using a Dependency Injection (DI) container, you might as well try to ask the container to tell what it constructed.

下图说明了从对象树的根部进行内省。

图 7.9 从根部反思对象树

Figure 7.9 Introspecting the object tree, from its root

让我们看一个嘻哈节拍循环的小型搜索引擎的示例。它从根本上由一个引擎组成,该引擎本身查询反向索引以实现快速搜索查询。出于索引目的,它还浏览服务用户贡献的链接存储库,使用循环分析器提取每个节拍循环的重要特征以放入反向索引中。该分析仪使用波形处理器。

Let’s look at an example of a little search engine of hip-hop beats loops. It’s made up of an engine, at the root, that itself queries a reverse index for fast search queries. For indexing purposes, it also browses a repository of links contributed by users of the service, using a loop analyzer to extract the significant features of each beats loop to put into the reverse index. The analyzer makes use of a waveform processor.

引擎、反向索引、链接存储库和循环分析器都是抽象接口,每个接口都有多个实现。对象树的精确连接是在运行时确定的,并根据环境的配置而变化。

The engine, reverse index, links repository, and loop analyzer are all abstract interfaces with more than one implementation each. The exact wiring of the object tree is determined at runtime and changes according to the environment’s configuration.

反思与反思

Introspecting with Reflection

如果它是一个对象,我们可以遍历它。

If it’s an object, we can traverse it.

-阿诺德·施瓦辛格

—Arnold Schwarzenegger

内省对象树只不过是一个简单的递归遍历。从给定的(根)实例中,您可以获得其类并枚举每个声明的字段,因为这就是类在此处存储其注入的协作者的方式。对于每个协作者,您通过递归调用来执行遍历。

Introspecting a tree of objects is nothing but a trivial recursive traversal. From the given (root) instance, you get its class and enumerate each declared field because that’s how classes store their injected collaborators here. For each collaborator, you carry out the traversal through a recursive call.

正如您可能怀疑的那样,您需要过滤掉不想包含在遍历中的无趣元素,例如字符串和其他低级内容等类。这里的过滤仅基于类的限定名称。如果与业务逻辑无关的类的实例作为参数传递,那么您只想忽略它。以下代码片段说明了如何在过滤不感兴趣的元素时进行内省:

As you might suspect, you need to filter uninteresting elements that you don’t want to include in the traversal—classes like strings and other low-level stuff. Here the filtering is just based on the qualified names of the classes. If an instance of a class that has nothing to do with the business logic is passed as a parameter, then you want to just ignore it. The following code snippet illustrates how to do that introspection while filtering uninteresting elements:

1 最终字符串前缀 = org.livingdocumentation.
可见的工作。”;
2
3 public void introspectByReflection(最终对象实例, int
深度)\
4 抛出 IllegalAccessException {
5 最终 Class<?> clazz = instance.getClass();
6 if (!clazz.getName().startsWith(prefix)) {
7返回;
8}
9     // System.out.println(indent(深度) + 实例);
10 for (字段字段: clazz.getDeclaredFields()) {
11 field.setAccessible(true);//必要
12 最终对象成员 = field.get(instance);
13 introspectByReflection(成员, 深度 + 1);
14}
15}
1  final String prefix = org.livingdocumentation.
visibleworkings.";
2
3  public void introspectByReflection(final Object instance, int
depth)\
4  throws IllegalAccessException {
5    final Class<?> clazz = instance.getClass();
6    if (!clazz.getName().startsWith(prefix)) {
7       return;
8    }
9    // System.out.println(indent(depth) + instance);
10   for (Field field : clazz.getDeclaredFields()) {
11    field.setAccessible(true);// necessary
12    final Object member = field.get(instance);
13    introspectByReflection(member, depth + 1);
14   }
15 }

使用此代码,如果您仅使用正确的缩进打印每个元素,控制台将显示以下内容:

With this code, if you just print each element with the proper indentation, the console displays the following:

1 单线程搜索引擎
2 ..InMemoryLink贡献
3 ..MockLoopAnalyzer
4 ....波形能量处理器
5 ..模拟反向索引
1  SingleThreadedSearchEngine
2  ..InMemoryLinkContributions
3  ..MockLoopAnalyzer
4  ....WaveformEnergyProcessor
5  ..MockReverseIndex

该引擎是一个单线程引擎,它使用贡献链接的内存存储库,以及循环分析器的模拟和反向索引的另一个模拟。

This engine is a single-threaded one, and it uses an in-memory repository of contributed links, together with a mock of a loop analyzer and another mock of a reverse index.

使用相同的代码,您可以构建一个包含每个元素以及它们之间的正确关系的点图,如图7.10所示。

With the same code, you can instead build a DOT diagram with each element and the proper relationships between them, as shown in Figure 7.10.

一个图代表在实践中内省对象树。

图 7.10 实践中内省对象树

Figure 7.10 Introspecting the object tree, in practice

该图显示的信息与我们之前控制台中的文本相同,但可见关系可以显示其他信息。

This diagram shows the same information as the text in the console we had before, but the visible relationship could show additional information.

不反思地内省

Introspecting Without Reflection

要在不使用反射的情况下内省对象树,树中的所有对象必须表现出一种可访问的方式来枚举其协作者。您可以使用公共字段来完成此操作,但我不建议这样做。相反,他们可以公开一个返回其成员列表的公共方法。

To introspect an object tree without using reflection, all the objects in the tree must exhibit an accessible way to enumerate their collaborators. You could accomplish this with public fields, but I do not recommend that. Instead, they can expose a public method that returns the list of their members.

在最简单的情况下,每个元素都会实现一些接口,例如Introspectable,成为复合模式的实例:

In the simplest case, every element would implement some interface like Introspectable, becoming an instance of the composite pattern:

1 个接口可自省 {
2 集合<?>members();
3 }
1  interface Introspectable {
2     Collection<?> members();
3  }

因此,树的遍历只不过是组合的递归遍历:

Thus the traversal of the tree would again be nothing but the recursive traversal of the composite:

1 private void traverseComposite(对象实例, int 深度) {
2 最终字符串名称=instance.getClass().getName();
3     // 将此节点添加到图中
4 digraph.addNode(name).setLabel(instance.toString());
5 if (!(instance instanceof Introspectable)) {
6返回;
7}
8 最终 Introspectable 内省 = (Introspectable)
实例;
9 for (对象成员: introspectable.members()) {
10 traverseComposite(成员, 深度 + 1);
11     // 添加从该节点到其成员的关系
图表
12 digraph.addAssociation(name, member.getClass().getName());
13}
14}
1  private void traverseComposite(Object instance, int depth) {
2    final String name = instance.getClass().getName();
3    // Add this node to the diagram
4    digraph.addNode(name).setLabel(instance.toString());
5  if (!(instance instanceof Introspectable)) {
6      return;
7  }
8  final Introspectable introspectable = (Introspectable)
instance;
9  for (Object member : introspectable.members()) {
10    traverseComposite(member, depth + 1);
11    // Add the relationship from this node to its member to
the diagram
12    digraph.addAssociation(name, member.getClass(). getName());
13   }
14  }

显然,这种方法产生的输出与使用反射获得的输出完全相同。

Obviously, this approach produces exactly the same output you get by using reflection.

您应该选择哪种方法?如果所有对象都是由团队创建的,并且数量不是太多,我建议使用复合风格,只要它不会过多地污染类。

Which approach should you choose? If all the objects are created by the team and there aren’t too many of them, I recommend the composite flavor, as long as it doesn’t pollute the classes too much.

在所有其他情况下,通过反思进行内省的方法是最好或唯一的选择。这种方法有助于使内部运作可见。对于针对每个给定业务请求即时构建的工作流、决策树或决策表,内省工作是一种使所构建的特定结构对用户和开发人员可见的方法。

In all other cases, the approach of introspection by reflection is the best or only choice. This approach helps make the inner workings visible. In the case of a workflow, decision tree, or decision table that is built on the fly for each given business request, introspectable workings is a way to make the particular structure that was built visible for users and developers alike.

然而,有时你根本不需要任何内省。当处理由配置、硬编码或来自文件或数据库驱动时,显示工作可能会被简化很多,因为这只是以良好的方式显示配置的一种方式。至少,由配置驱动的每个工作流或处理都应该能够显示用于特定类型处理的配置。

Sometimes, however, you don’t need any introspection at all. When the processing is driven by a configuration, hardcoded, or from a file or from a database, displaying the workings may be simplified a lot, as this is just a way to display the configuration in a nice way. At a minimum, every workflow or processing that is driven by a configuration should be able to display the configuration that is used for a particular type of processing.

概括

Summary

由于在运行时执行源代码时比从源代码存储库中的静态工件更容易获取某些知识,因此工作软件可以而且应该被视为出于文档目的的事实来源。随着分布式架构和云基础设施变得越来越普遍,利用运行时知识的机会也将越来越多,这些知识也可以由机器访问以获得更好的活文档。

Because some knowledge is much more easily accessible when the source code is executed at runtime than from the static artifacts in the source code repository, working software can, and should, be considered a source of truth for documentation purposes. And with distributed architecture and cloud infrastructure becoming more prevalent, so will the opportunities for exploiting runtime knowledge that is also accessible by machines for better living documentation.

第8章

Chapter 8

可重构文档

Refactorable Documentation

活文档是使用权威来源的最新知识自动生成的文档。相比之下,使用专有工具创建的文档必须手动更新,这是乏味的。在这两种极端情况之间是可重构文档,必须以智能方式手动更新,这要归功于减轻劳动密集型负担的自动化工具。

Living documents are documents that are automatically generated using up-to-date knowledge from an authoritative source. In contrast, documents created using proprietary tools must be updated manually, which is tedious. Between these two extreme cases are refactorable documents, which must be manually updated but in a smart way, thanks to automation tools that reduce the labor-intensive burden.

对于文本文档,支持重构的工具的一个示例是大多数文本编辑器的查找和替换功能,这使得一致地更改大型文档中使用的单词变得容易。例如,这对于维护仅不时变化的图表可能很有吸引力,其中每个单词由于单词之间的链接而被多次引用。

For text documents, one example of a tool that enables refactorability is the Find and Replace feature of most text editors, which makes it easy to consistently change a word used in a large document. For example, this can be appealing for maintaining diagrams that change only from time to time, where each word is referenced multiple times because of the links between the words.

但实现可重构性的工具的突出例子是源代码和应用在其上的自动重构。开发人员花费大量时间阅读代码并试图从中获取知识。如果您专门考虑代码的文档价值,那么它就会成为您的实时文档中的一个关键成分,一个很好地拥抱变化的成分。

But the prominent example of a tool that enables refactorability is source code and the automated refactorings that apply on it. Developers spend a lot of time reading code and trying to get knowledge out of it. If you think of code specifically for its documentation value, then it becomes a key ingredient in your living documentation, an ingredient that embraces change very well.

自动重构是更改软件系统的主要方法之一。在健康的项目中,这个过程一直在使用。当在某个地方重命名类或方法时,自动重构工具会立即更新声明及其在整个项目中使用的每个实例。将类型移动到另一个模块、向函数签名添加参数以及从代码块中提取函数只是工具自动执行的一些转换。这种重构自动化与测试相结合,使团队能够非常频繁地进行更改,包括重大更改,因为这些工具消除了痛苦。

Automated refactoring is one of the primary ways of changing a software system. In healthy projects, this process is used all the time. When renaming a class or a method somewhere, an automated refactoring tool updates the declaration and every instance of its use throughout the project—instantly. Moving a type to another module, adding a parameter to a function signature, and extracting a function out of a block of code are just a few transformations that tools do automatically. This refactoring automation, combined with tests, enables teams to make changes, including big changes, very frequently because the tools remove the pain.

一直重构是一件好事,即使它可以被视为对传统文档的挑战。活文档支持持续重构,并涉及利用自动重构的力量而不是抵制它们的文档方法。

Refactoring all the time is a good thing, even if it can be seen as a challenge for traditional documentation. Living documentation embraces continuous refactoring and involves documentation approaches that leverage the power of automated refactorings instead of resisting them.

代码即文档

Code as Documentation

程序是供人类阅读的,只是偶尔供计算机执行。

Programs are meant to be read by humans and only incidentally for computers to execute.

—Harold Abelson、Gerald Jay Sussman 和 Julie Sussman,《计算机程序的结构和解释》

—Harold Abelson, Gerald Jay Sussman, and Julie Sussman, Structure and Interpretation of Computer Programs

是的,但更多。源代码也是您拥有的任何集合中唯一保证准确反映现实的文档。因此,它是唯一已知真实的设计文件。设计师的想法、梦想和幻想只有在代码中才是真实的。大量 UML 中的图片只有在代码中才是真实的。源代码是其他文档无法声称的设计方式。另一种想法是:注释也许不在写作中,而在阅读中。1

That, yes, but more. The source code is also the ONLY document in whatever collection you may have that is guaranteed to reflect reality exactly. As such, it is the only design document that is known to be true. The thoughts, dreams, and fantasies of the designer are only real insofar as they are in the code. The pictures in the reams of UML are only veridical insofar as they are in the code. The source code is the design in a way that no other document can claim. One other thought: maybe gloss isn’t in the writing, maybe it’s in the reading.1

1. http://wiki.c2.com/?WhatIsSoftwareDesign

1.http://wiki.c2.com/?WhatIsSoftwareDesign

——罗恩·杰弗里斯

—Ron Jeffries

大多数时候,代码就是它自己的文档。当然,代码是为机器编写的,但代码也是为了人类能够理解它、维护和进化而编写的。

Most of the time, code is its own documentation. Code is written for machines, of course, but code is also written so that human beings can understand it for its maintenance and evolution.

提高代码被人们快速清晰理解的能力需要大量的技巧和技巧。这是软件工匠社区的核心主题,并且有许多关于该主题的书籍、文章和会议演讲,本书无意取代这些内容。相反,它重点关注一些与代码即文档本身的想法特别相关、典型或原创的实践和技术。正如克里斯·爱泼斯坦曾经在一次演讲中所说,“善待未来的自己。” 学习如何使代码易于理解将对你未来的自己有很大的好处。

It takes a lot of skills and techniques to improve the ability of code to be quickly and clearly understood by people. It’s a core topic in the software crafters community, and there have been many books, articles, and conference talks on the topic, which this book is not meant to replace. Instead it focuses on a few practices and techniques that are especially relevant, typical, or original with respect to the idea of code being documentation itself. As Chris Epstein once said during a talk, “Be kind to your future self.” Learning how to make code easy to understand will be a big bonus for your future self.

许多书籍都是关于编写易于阅读的代码的。特别重要的是Robert Cecil Martin(通常称为 Uncle Bob)的《简洁代码》和Kent Beck 的《实现模式》 。肯特提倡问自己:“当别人读到这段代码时,我想对他们说什么?” 以及“计算机会用这段代码做什么?” 以及“我怎样才能向人们传达我的想法?” 2

Many books have been written on writing code that is easy to read. Of particular importance are Clean Code by Robert Cecil Martin (commonly referred to as Uncle Bob) and Implementation Patterns by Kent Beck. Kent advocates asking yourself, “What do I want to say to someone when they read this code?” and “What will the computer do with this code?” and “How can I communicate what I am thinking to people?”2

2.贝克,肯特。实施模式。霍博肯:Addison-Wesley Professional,2007。

2.Beck, Kent. Implementation Patterns. Hoboken: Addison-Wesley Professional, 2007.

文字布局

Text Layout

我们通常将代码视为线性介质,但代码本身是文本编辑器二维空间中字符的图形排列。这种代码的二维布局可以用来表达含义。

We usually think of code as a linear medium, but code is itself a graphical arrangement of characters in the two-dimensional space of a text editor. This 2D layout of the code can be used to express meaning.

文本布局最常见的示例是类成员排序的准则:

The most common examples of text layout are the guidelines on the ordering of the members of a class:

  • 类声明

  • Class declaration

  • 领域

  • Fields

  • 构造函数和方法

  • Constructors and methods

通过这种排序,即使类被声明为纯文本,页面上文本块的分层也隐含了视觉效果。这与 UML 中类的直观表示方式相差不远(参见图 8.1)。代码布局和视觉符号之间的主要区别是代码中文本块周围没有边框线。

With this ordering, even as the class is declared as plain text, there is a visual aspect implied by the layering of the blocks of text on the page. This is not that far from how a class is visually represented in UML (see Figure 8.1). The main difference between code layout and visual notation is the absence of the border lines around the blocks of text in the code.

该图显示了类的 UML 视觉符号,分别由类名、燃料和方法组成。

图 8.1 类的 UML 视觉符号

Figure 8.1 UML visual notation for a class

以下各节将介绍代码布局的其他情况。

The following sections look at other cases of code layout.

表格代码布局

以被视为状态机的套接字为例。这个状态机可以通过它的状态转换表来完整描述,状态转换表可以按字面意思表达为代码。在这种情况下,布局确实很重要,包括当前状态、转换和下一个状态的垂直对齐,如图8.2所示。

Take the example of a socket considered as a state machine. This state machine can be fully described through its state transition table, which can be expressed literally as code. In this case, the layout really matters, including the vertical alignment of the current states, the transitions, and the next states, as shown in Figure 8.2.

下图显示了作为状态机的套接字的转换表及其富有表现力的代码布局。

图 8.2 套接字作为状态机的转换表及其表达性代码布局

Figure 8.2 The transition table of a socket as a state machine with its expressive code layout

这种类型的布局很容易用代码完成,只是 IDE 的自动代码格式化通常可能会破坏这种对齐方式。/**/在行首放置一个空注释部分 ( ) 可以防止格式化程序对行重新排序,但很难保留空格。当然,这一切都取决于您的 IDE 及其以更智能的方式自动格式化的功能。

This type of layout is easy to do with code, except that the automatic code formatting of the IDE may often break this alignment. Putting an empty comments section (/**/) at the beginning of a line can prevent the formatter from reordering the lines, but it’s hard to preserve the whitespace. Of course, this all depends on your IDE and its capabilities to autoformat in a smarter way.

安排-行动-断言

单元测试提供了如何使用代码的 2D 图形布局来表达含义的示例。Arrange-Act-Assert 约定提倡将代码组织在三个不同的部分中,每个部分都位于前一个部分的下方,如图8.3所示。

Unit tests offer examples of how the 2D graphical layout of code can be used to express meaning. The Arrange–Act–Assert convention advocates organizing code in three different sections, each below the previous one, as shown in Figure 8.3.

下图显示了单元测试中的 Arrange-Act-Assert 约定。

图 8.3 单元测试中的约定 Arrange-Act-Assert

Figure 8.3 The convention Arrange–Act–Assert in a unit test

当您熟悉此约定后,垂直布局使每个部分的作用以图形方式清晰可见。只需查看文本与空白的构成即可判断。

When you’re familiar with this convention, the vertical layout makes it graphically obvious what each section is doing. You can tell just by looking at the composition of text versus whitespace.

单元测试中的另一个约定涉及考虑单元测试将左侧的给定表达式与右侧的另一个表达式相匹配。在这种方法中,水平布局是有意义的:您希望将完整的断言放在一行上,并在断言的两侧放置两个表达式,如图 8.4 中的示例所示

Another convention in unit tests involves considering that a unit test matches a given expression on the left with another expression on the right. In this approach, the horizontal layout is meaningful: You want the full assertion on one single line, with the two expressions on both sides of the assertion, as shown in the example in Figure 8.4.

该图说明测试正在将左侧的表达式与右侧的表达式进行匹配。

图 8.4 测试是将左侧的表达式与右侧的表达式进行匹配

Figure 8.4 A test is about matching the expressions on the left with the expression on the right

关于以图形方式组织代码的各种可能的方法可以说得更多,但是本节只是为了引起您对这个可能性的关注。

Much more could be said about various possible ways to organize code graphically, but this section is just meant to draw your attention to this universe of possibilities.

编码约定

Coding Conventions

编程始终依赖约定来传达代码中的附加含义。编程语言语法可以完成很多工作。play()例如,在 C# 和 Java 中,很容易从变量中识别方法,play因为方法以括号结尾。但括号不足以表明类标识符和变量标识符之间的区别。因此,我们依靠命名约定(例如小写和大写的特定使用)来快速区分类名和变量名。此类公约如此普遍,以至于可以被认为是强制性的。

Programming has always relied on conventions to convey additional meaning in the code. The programming language syntax does a lot of the job. For example, in C# and Java, it’s easy to recognize the method play() from the variable play because methods end in parentheses. But the parentheses are not enough to indicate the difference between class identifiers and variable identifiers. As a result, we rely on naming conventions, such as particular uses of lowercase and uppercase, to quickly distinguish between class names and variable names. Such conventions are so ubiquitous that they can be considered mandatory.

例如,在 Java 中,类名必须混合大小写,每个内部单词的第一个字母大写(例如,StringBuilder)。这种约定有时称为CamelCase。实例变量遵循相同的约定,只是它们必须以小写首字母开头(例如,myStringBuilder)。另一方面,常量应全部大写,单词之间用下划线分隔(例如,DAYS_IN_WEEK)。当您熟悉这些约定时,您无需过多思考它们,并且您可以根据它们的大小写立即识别类、变量和常量。

For example, in Java, class names must be in mixed case, with the first letter of each internal word capitalized (for example, StringBuilder). This convention is sometime called CamelCase. Instance variables follow the same convention except that they must start with a lowercase first letter (for example, myStringBuilder). Constants, on the other hand, should be all uppercase with words separated by underscores (for example, DAYS_IN_WEEK). When you are familiar with such conventions, you don’t need to think too hard about them, and you instantly recognize classes, variables, and constants based on their case.

请注意,标准 Java 和 C# 符号对于 IDE 的着色和语法突出显示来说是多余的(实例变量为蓝色,静态变量带有下划线,等等)。因此,从理论上讲,您甚至不再需要命名约定。

Note that the standard Java and C# notations are redundant with the coloring and syntax highlighting of your IDE (instance variables are blue, static variables are underlined, and so on). So, in theory, you should not even need the naming convention any longer.

匈牙利表示法是使用命名约定来存储信息的一个极端示例。其中,变量或函数的名称表示该变量或函数的类型或预期用途。这个想法是将类型编码为短前缀,如以下示例所示:

The Hungarian notation is an extreme example of using a naming convention to store information. In it, the name of a variable or function indicates the type or intended use of that variable or function. The idea is to encode the type into a short prefix, as in these examples:

  • lPhoneNum:该变量是一个长整型 ( l)。

  • lPhoneNum: The variable is a long integer (l).

  • rgSamples:该变量是一个数组或一系列 Sample 元素 ( rg)。

  • rgSamples: The variable is an array or a range of Sample elements (rg).

这种表示法的明显缺点是它使标识符变得丑陋,就好像它们被混淆了一样。我绝对不推荐这个会议。

The visible drawback of this notation is that it makes identifiers ugly, as if they were obfuscated. I definitely do not recommend this convention.

约定不仅仅是为了方便,更是为了方便。它也是一种社会建构,是社区中所有开发人员之间的社会契约。当你熟悉一个惯例时,你会觉得很自在,甚至当你遇到不同的惯例时,你可能会感到不安。熟悉符号使其几乎不可见,即使对于那些不理解它的人来说它非常神秘。

A convention is more than just a matter of convenience; it’s also a social construct, a social contract between all developers in a community. When you are familiar with a convention, you feel at home with it, and you may even feel disturbed when you encounter a different convention. Familiarity with notation makes it almost invisible, even if it’s very cryptic to those who don’t understand it.

匈牙利表示法起源于缺乏类型系统的语言,使用这种表示法可以帮助您记住每个变量的类型。但是,除非您仍在使用 BCPL 进行编码,否则您不太可能需要这种表示法,因为它会极大地妨碍代码的可读性,而且几乎没有任何好处。

The Hungarian notation originated in languages that lack a type system, and using such notation helped you remember the type of each variable. However, unless you’re still coding in BCPL, it’s very unlikely that you need such notation because it impedes code readability too much, for almost no benefit.

警告

Caution

不幸的是,C# 保留了为每个接口添加前缀的惯例 I,因为这让人想起匈牙利表示法,并且没有任何好处。从设计的角度来看,我们甚至不应该知道一个类型是接口还是类;从调用者的角度来看这并不重要。事实上,您可以从一个类开始,然后在真正需要时将其概括为一个接口,并且这不会对代码进行太大改变。但是,它是应该遵循的标准约定的一部分,除非参与应用程序的所有开发人员都同意不这样做。

It’s unfortunate that C# has kept the convention of prefixing every interface with I, as this is reminiscent of Hungarian notation and has no benefit. From a design perspective, we should not even know whether a type is an interface or a class; it does not matter from a caller point of view. In fact, you might start with a class and later generalize it into an interface when really needed, and this should not change the code much. However, it’s part of the standard convention that should be followed, unless all developers involved in an application agree not to.

在没有内置命名空间支持的语言中,通常的做法是为所有类型添加特定于模块的前缀,如下所示:

In languages with no built-in support for namespaces, it’s common practice to prefix all types with a module-specific prefix, as shown here:

  • ACMEParser_Controller:模块ACMEParser

  • ACMEParser_Controller: Module ACMEParser

  • ACMEParser_Tokenizer:模块ACMEParser

  • ACMEParser_Tokenizer: Module ACMEParser

  • ACMECompiler_Optimizer:模块ACMECompiler

  • ACMECompiler_Optimizer: Module ACMECompiler

这通常是一种不好的做法,因为它会用可以在其包 (Java) 或命名空间 (C#) 中分解的信息污染类名:

This is usually a bad practice, as it pollutes the class names with information that could be factored out in their package (Java) or namespace (C#):

  • acme.parser:控制器

  • acme.parser: Controller

  • acme.parser:分词器

  • acme.parser: Tokenizer

  • acme.compiler:优化器

  • acme.compiler: Optimizer

正如您所看到的,编码约定尝试扩展编程语言的语法以支持缺少的功能和语义。当您没有类型时,您必须在命名约定的帮助下手动管理类型。另一方面,类型对于文档非常有帮助。

As you’ve seen, coding conventions try to extend the syntax of a programming language to support features and semantics that are missing. When you have no type, you must manage the type by hand with some help from the naming convention. On the other hand, types can be very helpful for documentation.

命名为主要文档

Naming as the Primary Documentation

在设计时寻找合适的词语是对时间的宝贵利用。

Searching for just the right words is a valuable use of time while designing.

—@Kent Beck 和 Ward Cunningham,“面向对象思维教学实验室”

—@Kent Beck and Ward Cunningham, “A Laboratory for Teaching Object-Oriented Thinking”

最重要的文档工具之一是命名。尽管它没有吸引力,但命名绝对不应该被忽视。通常,原作者赋予的名字是可用于检索这些作者知识的文档的唯一元素。好的命名非常重要。但良好的命名是困难的。名称作为一种社会惯例,需要达成共识并共享内涵。检查同义词库中是否有替代术语,积极倾听自发对话中使用的单词,并向同事询问有关姓名的反馈,这些都会有所帮助。

One of the most important documentation tools is naming. Despite its unattractiveness, naming should never be overlooked. More often than not, the names bestowed by the original authors are the only element of documentation available to retrieve those authors’ knowledge. Good naming is immensely important. But good naming is difficult. Names as a social convention need agreement and shared connotations. Checking a thesaurus for alternative terms, listening actively to the words used in spontaneous conversations, and asking your workmates feedback about names can help.

好名字不仅在阅读时有用,而且在阅读时也有用。当您搜索某些内容时它们也很有用。良好的命名可确保所有名称均可搜索。名为 Go 的编程语言提供了一个在可搜索性方面失败的命名示例,考虑到它源自名为 Google 的“搜索公司”,这一点尤其有趣。

Good names are not just useful when you read them; they are also useful when you’re searching for something. Good naming ensures that all names are searchable. The programming language named Go provides an example of naming that fails on the searchability front, which is especially interesting considering that it originates from the “search company” known as Google.

组合方法:您需要为它们命名

Composed Methods: You Need to Name Them

名字并不是孤立存在的。在面向对象的编程语言中,类名的集合形成了一种语言,并且单词之间具有各种关系,从而获得了整体的表现力。在论文“面向对象思维教学实验室”(1989)中,Kent Beck 和 Ward Cunningham 写道:

Names don’t live in isolation. In object-oriented programming languages, the set of class names form a language, and the words have various relationships to each other, gaining expressivity as a whole. In the paper “A Laboratory for Teaching Object-Oriented Thinking” (1989), Kent Beck and Ward Cunningham wrote:

对象的类名创建用于讨论设计的词汇表。事实上,许多人都指出,对象设计与语言设计比与过程程序设计有更多共同点。我们敦促学习者(并在设计时自己花费大量时间)找到合适的词语来描述我们的对象,一组在更大的设计环境中内部一致且令人回味的词语。3

The class name of an object creates a vocabulary for discussing a design. Indeed, many people have remarked that object design has more in common with language design than with procedural program design. We urge learners (and spend considerable time ourselves while designing) to find just the right set of words to describe our objects, a set that is internally consistent and evocative in the context of the larger design environment.3

3. Kent Beck 和 Ward Cunningham,“面向对象思维教学实验室”, http://t.co/PjQfDzRZcX

3.Kent Beck and Ward Cunningham, “A Laboratory for Teaching Object-Oriented Thinking,” http://t.co/PjQfDzRZcX

有关命名和实用建议的更多信息,我建议阅读 Tim Ottinger 在 Robert C. Martin 的书Clean Code中撰写的有关命名的章节。

For more on naming and practical advices, I suggest reading the chapter on naming written by Tim Ottinger in Robert C. Martin’s book Clean Code.

惯用命名是上下文相关的

Idiomatic Naming Is Contextual

命名风格不必在整个大型代码库中保持统一。系统的不同区域需要不同的惯用风格。例如,我总是在域模型或域层中使用业务域名(例如,Account, ContactBook, Trend)。但在基础设施层或适配器(在六边形架构意义上),我喜欢使用前缀和后缀来限定相应实现子类中使用的技术和模式(例如, 、MongoDBAccountSOAPBankHolidaysRepositoryPostgresEmailsDAOAdapterRabbitMQEventPublisher。在这个命名双重标准的示例中,名称必须说明域模型内的内容,而在域模型之外的基础设施代码中,名称必须说明内容如何它们已得到实施。

The naming style does not have to be uniform throughout a large code base. Different areas of the system call for different idiomatic styles. For example, I always go for business domain names within a domain model or in the domain layer (for example, Account, ContactBook, Trend). But on the infrastructure layer or adapters (in the hexagonal architecture sense), I like to use prefixes and suffixes to qualify technologies and patterns being used in the corresponding implementing subclasses (for example, MongoDBAccount, SOAPBankHolidaysRepository, PostgresEmailsDAOAdapter, RabbitMQEventPublisher). In this example of a double standard in naming, the names must tell what things are within a domain model, whereas outside of a domain model, in the infrastructure code, the names must tell how they are implemented.

针对框架进行编码

Coding Against a Framework

但如果你编写一个“没有框架”的应用程序,你最终会得到一个不明确的、未记录的、非正式的框架。

But if you write an app “without a framework,” you end up with an under-specified, un-documented, informal framework.

- 黑客新闻,https://news.ycombinator.com/item?id=10839081

—Hacker News, https://news.ycombinator.com/item?id=10839081

针对流行或固执己见的框架进行编码对于强大的文档具有巨大的价值。未编写的代码不需要文档。当您使用现成的框架(例如 Spring Boot(轻量级微服务框架)或 Axon-Framework(事件源应用程序框架))时,已经编写了很多代码,并且您的代码必须符合该框架期望。对于成熟度有限的团队来说,选择这样的框架可能是一个好主意,因为该框架将限制设计遵循某种结构。这听起来可能是一件坏事,但从知识转移的角度来看,这是一件好事:惊喜的空间较小,当您熟悉框架时,您可以理解大部分代码。此外,此类框架有详细记录,

Coding against a popular or opiniated framework has great value for strong documentation. Code that is not written needs no documentation. When you use an off-the-shelf framework such as Spring Boot (a lightweight microservice framework) or Axon-Framework (a framework for event-sourced applications), a lot of code is already written, and your code has to conform to what the framework expects. Choosing such a framework may be a good idea for a team of limited maturity, where the framework will constrain the design to follow some structure. This might sound like a bad thing, but it’s a good thing from a knowledge transfer perspective: There is less room for surprise, and when you’re familiar with the framework, you can understand most of the code. In addition, such frameworks are well documented, and their use of annotations also provides documentation in the code, as shown in the following example:

1 @CommandHandler
2 无效 placePurchase(PlacePurchaseCommand c){...}
1  @CommandHandler
2  void placePurchase(PlacePurchaseCommand c){...}

类型驱动文档

Type-Driven Documentation

类型是为开发人员和工具存储和传达知识的强大工具。使用类型系统,您不需要匈牙利符号;类型系统知道那里是哪种类型。无论是编译时(Java、C#)还是运行时(TypeScript)系统,它都是文档的一部分。

Types are powerful vehicles for storing and conveying knowledge for both developers and tools. With a type system, you need no Hungarian notation; the type system knows which type is there. It’s part of the documentation, whether it is a compile time (Java, C#) or runtime (TypeScript) system.

在 Java 或 C# IDE 中,您可以通过将鼠标放在所有内容上来查看其类型,并且工具提示会告诉您其类型。

In a Java or C# IDE, you can see the type of everything by putting the mouse over it, and a tooltip will tell you about its type.

基元是类型,但是当您使用自定义类型而不是基元时,类型才会真正发挥作用。例如,下面的代码并不能说明这个数量应该代表一定金额的全部情况,您需要一个注释来告诉货币:

Primitives are types, but types really shine when you use custom types instead of primitives. For example, the following code does not tell the whole story that this quantity is supposed to represent an amount of money, and you need a comment to tell the currency:

1 int 金额 = 12000;// 欧元
1  int amount = 12000; // EUR

但是,如果您创建自定义类型Money(例如作为类),它就会变得显式。现在您知道这是一笔钱,并且货币是代码的一部分:

But if you create a custom type Money, as a class, for example, it becomes explicit. Now you know it’s an amount of money, and the currency is part of the code:

1 金额 = Money.inEUR(12000);
1  Money amount = Money.inEUR(12000);

为不同的概念创建类型有很多优点,文档是非常重要的一项。这不再是一个随机整数,而是一笔钱,类型系统知道这一点并且可以告诉你。

There are many advantages to creating types for different concepts, and documentation is a very important one. This is not a random integer anymore, it’s an amount of money, the type system knows that and can tell you.

您还可以检查Money类型以了解更多信息。例如,以下是该类的 Javadoc 注释描述:

You can also check the Money type to learn more about it. For example, here is the class’s Javadoc comment description:

1 /**
2 * 欧元 (EUR) 货币数量,
3 * 用于会计目的,
4 * 即精确度为 1 美分。
5 *
6 * 不适合超过 1 万亿欧元的金额。
7 */
8类钱{
9 ...
10}
1  /**
2  * A quantity of money in Euro (EUR),
3  * for accounting purpose,
4  * i.e. with an accuracy of exactly 1 cent.
5  *
6  * Not suited for amounts over 1 trillion EUR.
7  */
8  class Money {
9  ...
10 }

这是有价值的信息,最好位于代码本身中,而不是其他地方的随机文档中。

This is valuable information, and it’s best located in the code itself rather than in random document somewhere else.

您的类型是文档的重要组成部分。输入所有内容并仔细命名类型。

Your types are an essential part of your documentation. Type everything and name the types carefully.

因此:尽可能使用类型。类型越强越好。避免裸基元和裸集合。将他们提升为一流类型。根据通用语言仔细命名您的类型,并在类型本身上添加足够的文档。

Therefore: Use types whenever possible. The stronger the types are, the better. Avoid bare primitives and bare collections. Promote them into first-class types. Name your types carefully, according to the ubiquitous language, and add just enough documentation on the types themselves.

从基元到类型

From Primitives to Types

在以下示例中,代码打开String; 它是一种类型,但是是一种弱类型,在实践中几乎就像一个原语:

In the following example, the code switches on a String; it’s a type, but a weak one, which in practice is almost like a primitive:

1 验证(字符串状态)
2 if (状态==“ON”)
3 ...
4 if(状态==“关闭”)
5 ...
6 其他
7       // 一些错误信息
1  validate(String status)
2    if (status == "ON")
3    ...
4    if (status == "OFF")
5    ...
6    else
7      // some error message

这种代码是可耻的。因为 aString可以是任何值,所以您需要一个附加else子句来捕获任何意外值。所有这些代码都描述了预期的行为,但如果此行为是由类型系统完成的(例如,通过使用类型化枚举),则根本不需要编写任何代码:

This kind of code is shameful. Because a String can be anything, you need an additional else clause to catch any unexpected value. All this code describes the expected behavior, but if this behavior were done by the type system—for example, by using a typed enum—there would simply be no code to write at all:

1个开关(Status状态){
2 种情况:开...
3 种情况:关闭...
4 }
1  switch (Status status){
2    case: ON ...
3    case: OFF ...
4  }

记录类型和集成文档

Documented Types and Integrated Documentation

类型是在 Javadoc 部分或其 C# 等效项中放置有关概念的文档的完美位置。此类文档将在类型的整个生命周期中不断发展:它是在创建类型时创建的,如果类型被删除,其文档将随之消失。如果该类型被重命名或移动,其文档仍保留在其上,因此无需维护。

A type is a perfect place to put documentation about a concept in a Javadoc section or its C# equivalent. Such documentation will evolve throughout the life of the type: It’s created when the type is created, and if the type is deleted, its documentation will go away with it. If the type is renamed or moved, its documentation remains attached to it, so there is no maintenance.

唯一的风险是,如果您更改类型的定义而不更改其文档,您可能仍然会得到误导性的文档。然而,这种风险较低,因为文档与类型声明位于同一位置。

The only risk is that if you change the definition of the type without changing its documentation, you might still end up with misleading documentation. However, this risk is lower because the documentation is co-located with the type declaration.

将类型与其文档一起使用的一个明显好处是,它可以直接在 IDE 中为您提供集成文档。当您将鼠标悬停在代码中任意位置的类型名称上时,IDE 会显示一个包含相关文档的小弹出窗口。当您使用自动完成功能时,每个自动完成选项前面都会显示文档的简短摘录。

An obvious benefit of using types with their documentation is that it gives you integrated documentation directly within your IDE. When you mouse over a type name anywhere in the code, the IDE shows a small popup with the related documentation. When you’re using autocompletion, a brief excerpt of the documentation is shown in front of each autocompletion option.

类型和关联

Types and Associations

代码中的关联表示为类型的成员字段。代码及其类型可以说明很多信息,但有时您还需要更多信息。让我们考虑几个例子。当关联是一对一的,并且成员字段被正确命名时,您只需要这样:

Associations in code are expressed as member fields to types. The code and its types can tell a lot, but sometimes you need something more. Let’s consider a few examples. When the associations are one-to-one, and the member fields are well named, you need nothing more than this:

1   // 没什么可说的
2 私人最终地点;
3 私人最终地点;
1  // nothing to say
2  private final Location from;
3  private final Location to;

当类型本身也可以表达含义时,无需多说。在下面的示例中,注释与声明的类型是多余的,众所周知,aSet强制执行唯一性:

There is no need to tell much when types can also express meaning themselves. In the following example, the annotation is redundant with the declared type, and it is common knowledge that a Set enforces unicity:

1 @Unicity
2 个私人最终 Set<Currency> 货币;
1  @Unicity
2  private final Set<Currency> currencies;

类似地,以下代码不需要额外的排序声明,因为它是由具体类型暗示的(但从调用者的角度来看确实是这样吗?):

Similarly, the following code does not need the additional ordering declaration, as it is implied by the concrete type (but is this really the case from the caller point of view?):

1 @已订购
2 Collection<Item> items = new TreeSet<Item>();
1  @Ordered
2  Collection<Item> items = new TreeSet<Item>();

您可以重构为新的声明类型以使文档变得多余:

You could refactor into a new declared type to make the documentation redundant:

1 SortedSet<Item> items = new TreeSet<Item>();
1  SortedSet<Item> items = new TreeSet<Item>();

但这样做会暴露很多您可能不想暴露的方法。如果您只想公开Iterable<Item>,则顺序是内部细节。

But doing this exposes a lot of methods you may not want to expose. If you would like to expose only Iterable<Item>, the ordering is an internal detail.

您可以在这里看到我更喜欢类型而不是注释。

You can see here that I prefer types over annotations.

类型胜过评论

Types over Comments

评论可能而且经常会撒谎。命名也是如此,尽管程度较小。但类型不会说谎;如果他们这样做,程序甚至无法编译。

Comments can and often do lie. So does naming, though to a lesser extent. But types don’t lie; if they did, the program would not even compile.

方法名称可以假装如下:

A method name may pretend to be the following:

1 获取城市客户()
1  GetCustomerByCity()

但无论其名称如何,如果签名及其类型实际上如下所示,您就可以更准确地了解它的真实含义:

But regardless of its name, if the signature and its types are actually as follows, you get a much more accurate picture of what it really is:

1 List<Prospect> 函数(邮政编码)
1  List<Prospect> function(ZipCode)

它甚至可以改进:List<Prospect>本身可以是一种类型,例如Prospectsor ProspectionPortfolio

And it could even be improved: List<Prospect> could be a type in itself, something like Prospects or ProspectionPortfolio.

仅使用原语,您就可以自行决定是否可以信任该命名。布尔值是什么ignoreOrFail意思?枚举,例如IGNOREFAIL,增加了准确性。

With just primitives, you’re on your own to decide whether you can trust the naming or not. What does the Boolean ignoreOrFail mean? Enums, such as IGNORE and FAIL, add accuracy.

Optional<Customer>完全准确地表达可能不存在结果的情况。在支持它们的语言中,单子完全准确地表明副作用的存在。在这些示例中,信息是准确的,因为编译器强制执行它。

Optional<Customer> expresses the possible absence of a result with total accuracy. In languages that support them, monads signal the presence of side effects with total accuracy. In these examples, the information is accurate because the compiler enforces it.

无论变量名称如何,诸如此类的泛型都Map<User, Preference>可以说明很多信息。

Generics such as Map<User, Preference> tell a lot, whatever the variable name.

如果您仍然不相信,您可以阅读主题为“关于静态类型和动态类型之间的差异我们真正了解什么?”的研究。4

In case you’re still not convinced, you can read this study on the topic: “What Do We Really Know About the Differences Between Static and Dynamic Types?”4

4. Stefan Hanenberg, http://www.slideshare.net/mobile/devnology/what-do-we-really-know-about-the-differences- Between-static-and-dynamic-types

4.Stefan Hanenberg, http://www.slideshare.net/mobile/devnology/what-do-we-really-know-about-the-differences-between-static-and-dynamic-types

类型驱动开发

使用类型时,即使您没有命名变量,由于它们的类型,您仍然可以确定有关它们的很多信息。考虑以下变量声明:

When using types, even if you didn’t name the variables, you could still determine a lot about them, thanks to their type. Consider the following variable declaration:

1 FuelCardTransactionReport x = ...
1  FuelCardTransactionReport x = ...

类型名称说明了一切。仅当作用域中同时存在多个实例时,变量名称才有用。

The type name tells it all. The variable name will be useful only if there’s more than one instance in the scope at the same time.

函数和方法也是如此。即使不知道它的名称,您也可以看出一个作为ShoppingCart参数并返回的函数Money可能与定价或税收计算有关。只需查看函数签名,您就可以很好地了解该函数的功能。

The same goes for functions and methods. Even without knowing its name, you can tell that a function that takes ShoppingCart as an argument and returns Money probably has something to do with pricing or tax calculation. By just looking at the function signature, you can glean a good understanding of what the function can do.

另一方面,如果您尝试查找执行购物车定价的代码,您有两种选择:

On the other hand, if you’re trying to find the code doing the pricing of a shopping cart, you have two options:

  • 猜测类或方法的命名方式并根据您的猜测执行搜索

  • Guess how the class or method is named and perform a search based on your guess

  • 根据类型猜测签名并按签名执行搜索

  • Guess the signature in terms of type and perform a search by signature

Haskell 有一个名为 Hoogle 的文档工具,可以显示具有给定签名的每个函数。在使用 Eclipse (Kepler) 的 Java 中,您还可以通过方法签名进行搜索。在搜索菜单中,选择“Java 搜索”选项卡,选择单选按钮“搜索:方法”和“限制为:声明”,然后输入搜索字符串(参见图 8.5

Haskell has a documentation tool called Hoogle that can show every function with a given signature. In Java using Eclipse (Kepler), you can also search by method signature. In the search menu, you select the Java Search tab, select the radio buttons Search For: Method and Limit To: Declarations, and then type in the search string (see Figure 8.5):

下图说明了 Eclipse 中按方法签名进行搜索。

图8.5 在Eclipse中按方法签名搜索

Figure 8.5 Search by method signature in Eclipse

1 *(int, int) 整数
1  *(int, int) int

您会得到很多以两个整数作为参数并返回另一个整数的方法的搜索结果,例如:

You get a lot of search results of methods that take two integers as parameters and return another integer, for example:

1 com.sun.tools.javac.util.ArrayUtils
                         .calculateNewLength(int, int) int
2 com.google.common.math.IntMath.mean(int, int) int
3 com.google.common.primitives.Ints.compare(int, int) int
4 org.apache.commons.lang3.RandomUtils.nextInt(int, int) int
5 org.joda.time.chrono.BasicChronology
                         .getDaysInYearMonth(int, int) int
6 ...
1 com.sun.tools.javac.util.ArrayUtils
                         .calculateNewLength(int, int) int
2 com.google.common.math.IntMath.mean(int, int) int
3 com.google.common.primitives.Ints.compare(int, int) int
4 org.apache.commons.lang3.RandomUtils.nextInt(int, int) int
5 org.joda.time.chrono.BasicChronology
                         .getDaysInYearMonth(int, int) int
6 ...

这不仅适用于整数等基元,也适用于任何类型。Coordinates例如,如果您正在寻找计算两个对象 ( Latitude, )之间距离的方法Longitude,您将使用完全限定类型名称搜索以下签名:

This works not just for primitives such as integers but for any type. For example, if you were looking for a method to calculate the distance between two Coordinates objects (Latitude, Longitude), you would search for the following signature, using the fully qualified type names:

1 *(flottio.domain.坐标, flottio.domain.坐标)
双倍的
1  *(flottio.domain.Coordinates, flottio.domain.Coordinates)
double

这将找到您正在寻找的服务,而无需知道其名称:

This would find the service you are looking for, without knowing its name:

1 GeoDistance.distanceBetween(坐标, 坐标) 双
1  GeoDistance.distanceBetween(Coordinates, Coordinates) double

您可能听说过类型驱动开发 (TDD) 或类型优先开发 (TFD)。这些方法对于类型有相似的想法。

You might have heard about type-driven development (TDD), or type-first development (TFD). These approaches have similar ideas about types.

组合方法

The Composed Method

清晰的代码,就像清晰的写作一样,很难做到。通常,只有当其他人看到它时,你才能知道如何说清楚,或者你稍后再回来看它。

Clear code, like clear writing, is hard to do. Often you can only tell how to make it clear when someone else looks at it, or you come back to it at a later date.

沃德·坎宁安是这样解释的。每当你必须弄清楚代码在做什么时,你就在头脑中建立了一些理解。一旦你构建了它,你应该将这种理解转移到代码中,这样就没有人需要再次在头脑中从头开始构建它。

Ward Cunningham explained it like this. Whenever you have to figure out what code is doing, you are building some understanding in your head. Once you’ve built it, you should move that understanding into the code so nobody has to build it from scratch in their head again.

——马丁·福勒,《重构》

—Martin Fowler, “Refactoring”

清晰的代码并不是偶然发生的。你必须利用你所有的设计技能,通过不断的重构来让它出现。例如,遵循 Kent Beck 表达的简单设计的四个规则可能是一个好主意。5 , 6

Clear code does not happen by chance. You have to make it emerge through continuous refactoring, using all your design skills. For example, it could be a good idea to follow the four rules of simple design expressed by Kent Beck.5, 6

5. Martin Fowler,ThoughtWorks, http://martinfowler.com/bliki/BeckDesignRules.html

5.Martin Fowler, ThoughtWorks, http://martinfowler.com/bliki/BeckDesignRules.html

6. Corey Haines,“理解简单设计的四个规则”, https://leanpub.com/4rulesofsimpledesign

6.Corey Haines, “Understanding the Four Rules of Simple Design,” https://leanpub.com/4rulesofsimpledesign

在所有设计技能中,组合方法模式与文档目的特别相关。例如,这段代码在做什么?

Among all the design skills, the composed method pattern is particularly relevant for documentation purposes. For example, what’s this block of code doing?

  • 它正在压扁纤维条。

  • It’s squishing the fibbly-bar.

  • 那么我们应该将其提取到一个squishFibblyBar函数中吗?

  • So should we extract it into a squishFibblyBar function?

组合方法是编写清晰代码的基本技术。它涉及将代码划分为许多小方法,每个小方法执行一项任务。由于每个方法都有命名,因此方法名称是主要文档。

The composed method is an essential technique for writing clear code. It involves dividing code into a number of small methods, each of which performs one task. Because each method is named, method names are the primary documentation.

常见的重构是将需要注释的代码块替换为以注释命名的组合方法。考虑以下示例:

A common refactoring is to replace a block of code that requires a comment into a composed method named after the comment. Consider the following example:

1 公共仓位 equalPosition(Trade... trades) {
2          // 如果交易列表没有交易
3 if (trades.length == 0) {
4                   // 返回数量零的位置
5 返回新的Position(0);
6 }
7          // 第一笔交易的返回数量
8 return new Position(trades[0].getQuantity());
9 }
1  public Position equivalentPosition(Trade... trades) {
2         // if trades list has no trade
3         if (trades.length == 0) {
4                  // return position of quantity zero
5                  return new Position(0);
6         }
7         // return quantity of first trade
8         return new Position(trades[0].getQuantity());
9  }

这里的注释建议您可以做得更好,例如通过简化代码或将方法提取为组合方法。您可以将小的内聚代码块提取到它们自己的组合方法中,如下所示:

Here the comments suggest that you can do better, such as by simplifying the code or extracting methods into composed methods. You could extract little cohesive blocks of code into their own composed method, as shown here:

1 公共仓位 equalPosition(Trade... trades) {
2 if (hasNo(交易)) {
3 返回positionZero();
4 }
5 返回新仓位(quantityOfFirst(trades));
6 }
7
8 //----
9
10 private boolean hasNo(Trade[] trades) {
11 次退货交易.length == 0;
12}
13
14 私人持仓positionZero() {
15 返回新位置(0);
16}
17 号
18 private static double amountOfFirst(Trade[] trades) {
19 次退货交易[0].getQuantity();
20}
1  public Position equivalentPosition(Trade... trades) {
2         if (hasNo(trades)) {
3                return positionZero();
4         }
5         return new Position(quantityOfFirst(trades));
6  }
7
8  //----
9
10 private boolean hasNo(Trade[] trades) {
11       return trades.length == 0;
12 }
13
14 private Position positionZero() {
15       return new Position(0);
16 }
17
18 private static double quantityOfFirst(Trade[] trades) {
19       return trades[0].getQuantity();
20 }

请注意,第一个方法现在描述了整体处理,下面的其他三个方法描述了代码的低级部分。这是通过将方法组织到不同抽象级别来使代码清晰的另一种方法。

Notice that the first method now describes the overall processing, and the other three methods underneath describe low-level parts of the code. This is another way to make code clear by organizing the methods into different levels of abstraction.

这里第一个方法是高于其他三个方法的一个抽象级别。通常你只需阅读更高抽象级别的代码即可理解它可以做什么,而不必处理较低抽象级别中的所有代码。这使您可以更有效地阅读和导航未知代码。

Here the first method is one level of abstraction above the three other methods. Usually you can just read the code in the higher level of abstraction to understand what it does without having to deal with all the code in the lower levels of abstraction. This allows you to more efficiently read and navigate unknown code.

上面的代码还说明了文本布局的意义:只需通过方法的顺序,您就可以以图形方式看到两个抽象级别,一个位于另一个之上。

The code above also illustrates how the layout of text is meaningful: You can graphically see the two levels of abstraction one on top of the other, just through the ordering of the methods.

流畅的风格

Fluent Style

使代码更具可读性的最明显方法之一是使用称为流畅界面的风格来模仿自然语言。让我们考虑一个取自软件应用程序的示例来计算手机账单:

One of the most obvious way to make code more readable is to make it mimic natural language, using a style that is called a fluent interface. Let’s consider an example taken from a software application to calculate mobile phone billing:

1 Pricing.of(PHONE_CALLS).is(inEuro().withFee(12.50).
atRate(0.35));
1  Pricing.of(PHONE_CALLS).is(inEuro().withFee(12.50).
atRate(0.35));

你可以很容易地用英语读到:“电话的定价以欧元为单位,费用为 12.50,费率为 0.35。”

You can read this pretty easily in English: “The pricing of phone calls is in euros, with a fee of 12.50, at a rate of 0.35.”

代码可以变得更大,同时保持准英语句子的可读性:

The code can grow bigger while remaining readable as a quasi-English sentence:

1 (PHONE_CALLS) 的定价
2 .is(inEuro().withFee(12.50).atRate(0.35))
3 .and(TEXT_MESSAGE)
4 .are(inEuro().atRate(0.10).withInincluded(30));
1  Pricing.of(PHONE_CALLS)
2     .is(inEuro().withFee(12.50).atRate(0.35))
3     .and(TEXT_MESSAGE)
4       .are(inEuro().atRate(0.10).withIncluded(30));

使用内部 DSL

Using an Internal DSL

使用内部特定领域语言 (DSL) 通常在很大程度上依赖于方法链接以及其他技巧。流畅的界面是基于编程语言本身构建的内部 DSL 的一个示例。优点是您可以获得表达的力量,而无需放弃编程语言的所有优点:编译器检查、自动完成、自动重构功能等等。

Using an internal domain-specific language (DSL) usually relies heavily on method chaining, among other tricks. A fluent interface is an example of an internal DSL that is built on the programming language itself. The advantage is that you get the power of expression without giving up all the good things around your programming language: compiler checking, autocompletion, automated refactoring features, and  so on.

创建一个漂亮的流畅界面需要一些时间和精力,所以我不建议将其作为所有情况下的默认编程风格。它对于您发布的界面、向所有用户公开的 API、有关配置的任何内容以及测试尤其有趣,以便测试成为任何人都可读的活文档。

Creating a nice fluent interface takes some time and effort, so I do not recommend making it the default programming style in all situations. It’s especially interesting for your published interface, the API you expose to all your users, anything about configuration, and for testing so that the tests become living documentation that is readable by anyone.

.Net 中流畅接口的一个著名示例是 LINQ 语法。它是通过扩展方法实现的,并且能够非常接近地模仿 SQL,如本示例所示:

A famous example of a fluent interface in .Net is the LINQ syntax. It’s implemented through extension methods, and it manages to mimic SQL quite closely, as shown in this example:

1 列表<字符串> 国家/地区 = 新列表<字符串>
2 {“美国”、“加拿大”、“法国”、“英国”、“中国”、“俄罗斯”};
3
4 // 查找包含字母“C”的每个国家/地区,
5 // 按长度排序
6 IEnumerable<string>filteredOrdered = 国家/地区
7 .Where (c => c.Contains("C"))
8.OrderBy(c => c.Length);
9
10
1  List<string> countries = new List<string>
2   {"USA", "CANADA", "FRANCE", "ENGLAND","CHINA", "RUSSIA"};
3
4  // Find every country containing the letter 'C',
5  // ordered by length
6  IEnumerable<string> filteredOrdered = countries
7                           .Where (c => c.Contains("C"))
8                           .OrderBy(c => c.Length);
9
10

这是用于数据验证的流畅接口的另一个示例,来自 FluentValidation:7

Here’s another example of a fluent interface for data validation, from FluentValidation:7

7. https://github.com/JeremySkinner/FluentValidation

7.https://github.com/JeremySkinner/FluentValidation

1 使用FluentValidation;
2
3 公共类CustomerValidator: AbstractValidator<Customer> {
4 公共客户验证器(){
5 RuleFor(客户 => 客户.姓氏).NotEmpty();
6 RuleFor(客户 => 客户.Forename).NotEmpty()
7 .WithMessage("请指定名字");
8 RuleFor(客户 => 客户.Discount).NotEqual(0)
9 .When(客户=>客户.HasDiscount);
10 RuleFor(客户 => 客户.地址).Length(20, 250);
11 ...
12}
1 using FluentValidation;
2
3 public class CustomerValidator: AbstractValidator<Customer> {
4    public CustomerValidator() {
5      RuleFor(customer => customer.Surname).NotEmpty();
6      RuleFor(customer => customer.Forename).NotEmpty()
7                  .WithMessage("Please specify a first name");
8      RuleFor(customer => customer.Discount).NotEqual(0)
9                  .When(customer => customer.HasDiscount);
10     RuleFor(customer => customer.Address).Length(20, 250);
11     ...
12  }

实现流畅的界面

Implementing a Fluent Interface

正如在 TDD 中编写测试的第一步一样,在实现流畅的界面时,您从梦想开始。想象一下理想的流畅界面已经存在并且完美,即使您还没有开始构建它,也可以编写使用它的示例。然后选取其中的一个子集并开始使其发挥作用。你会遇到困难,迫使你重新考虑表达相同行为的替代方式。Martin Fowler 对于流畅的界面有更多话要说。8

As in the first step of writing tests in TDD, when implementing a fluent interface you start by dreaming. Write examples of using the ideal fluent interface by imagining that it’s there and perfect, even though you haven’t started to build it yet. Then take a subset of it and start to make it work. You’ll encounter difficulties that will force you to reconsider alternative ways to express the same behavior. Martin Fowler has more to say about fluent interfaces.8

8. Martin Fowler,ThoughtWorks, http://martinfowler.com/bliki/FluentInterface.html

8.Martin Fowler, ThoughtWorks, http://martinfowler.com/bliki/FluentInterface.html

流利测试

Fluent Tests

流畅的风格在测试中特别受欢迎。JMock、AssertJ、JGiven 和 Nfluent 是众所周知的库,可以帮助您以流畅的方式编写测试。当测试易于阅读时,它们就成为软件行为的文档。

The fluent style is particularly popular for testing. JMock, AssertJ, JGiven, and NFluent are well-known libraries that can help you write tests in a fluent style. When tests are easy to read, they become the documentation of the behaviors of the software.

Nfluent 9是由 Thomas Pierrain 创建的 C# 测试断言库。使用 NFluent,您可以以流畅的方式编写测试断言,如下所示:

NFluent9 is a test assertion library in C# created by Thomas Pierrain. Using NFluent, you can write test assertions in a fluent way, like this:

9. http://www.n-fluent.net

9.http://www.n-fluent.net

1 个整数?一 = 1;
2 Check.That(one).HasAValue().Which.IsPositive()
3.And.IsEqualTo(1);
1 int? one = 1;
2 Check.That(one).HasAValue().Which.IsPositive()
3           .And.IsEqualTo(1);

通过方法链接和许多其他技巧(特别是围绕 C# 泛型),该库允许进行非常易读的测试风格,如下所示:

Through method chaining and many other tricks—in particular around the C# generics—the library allows for a very readable style of tests, as shown here:

1 var 英雄 = "蝙蝠侠和罗宾";
2 检查.That(英雄).Not.Contains(“小丑”)
        .And.StartsWith("蝙蝠")
        .And.Contains("罗宾");
1  var heroes = "Batman and Robin";
2  Check.That(heroes).Not.Contains("Joker")
        .And.StartsWith("Bat")
        .And.Contains("Robin");

Java 中的一个等效库是 AssertJ。10

An equivalent library in Java is AssertJ.10

10.AssertJ http://joel-costigliola.github.io/assertj/

10.AssertJ, http://joel-costigliola.github.io/assertj/

创建 DSTL

Creating a DSTL

您可以创建自己的特定于域的测试语言 (DSTL),以便能够用纯代码编写漂亮的场景。这涉及测试数据构建器。

You can create your own domain-specific test language (DSTL) to enable writing pretty scenarios in plain code. This involves test data builders.

使用构建器时,创建用于创建测试数据的内部 DSL 并不是很困难。Nat Pryce 将此称为测试数据生成器。您可以使用测试数据生成器来扩展前面的示例,以在给定部分上创建对象。

When using builders, it is not very difficult to create an internal DSL for creating test data. Nat Pryce calls this a test data builder. You could extend the previous example with the use of a test data builder to create objects on the given section.

测试数据构建器可以嵌套。例如,您可以将捆绑旅行定义为将航班、住宿和附加服务组合到一个篮子中的旅行这样购买起来更方便。您可以使用测试数据生成器独立创建每个元素:

Test data builders can be nested. For example, you can define bundled travel as travel that groups flights, accommodations, and additional services into one basket so that it’s more convenient to buy. You can use a test data builder to create each element independently:

1 aFlight().from("CDG").to("JFK")
2 .withReturn().inClass(COACH).build();
3
4 酒店房间(“Radisson Blue”)
5 .from("12/11/2014").forNights(3)
6 .withBreakfast(欧陆式).build();
1 aFlight().from("CDG").to("JFK")
2     .withReturn().inClass(COACH).build();
3
4 anHotelRoom("Radisson Blue")
5     .from("12/11/2014").forNights(3)
6     .withBreakfast(CONTINENTAL).build();

您可以使用另一个测试数据生成器从每个产品创建捆绑包:

You could use another test data builder to create the bundle from each product:

1 aBundledTravel(“纽约蓝色周末”)
2 .with(aFlight().from("CDG").to("JFK")
3 .withReturn().inClass(COACH).build())
4 .与(
5 酒店房间(“Radisson Blue”)
6 .from("12/11/2014").forNights(3)
7 .withBreakfast(欧陆式).build()).build();
1  aBundledTravel("Blue Week-End in NY")
2    .with(aFlight().from("CDG").to("JFK")
3    .withReturn().inClass(COACH).build())
4  .with(
5    anHotelRoom("Radisson Blue")
6    .from("12/11/2014").forNights(3)
7    .withBreakfast(CONTINENTAL).build()).build();

测试数据构建器非常有用,您可能决定将它们不仅仅用于测试。例如,我最终将它们移至生产代码中,并确保它们不再是“测试”数据构建器,而只是常规的配套构建器,其中没有任何特定于测试的内容。

Test data builders can be so useful that you may decide to use them not just for tests. For example, I ended up moving them into the production code and made sure they were no longer “test” data builders but just regular companion builders with nothing test-specific in them.

有关 DSL 的更多信息,请参阅 Martin Fowler 的书Domain-Specific Languages 。

See Martin Fowler’s book Domain-Specific Languages for more on DSL.

何时不使用 Fluent 样式

When Not to Use a Fluent Style

流畅本身并不是目的,并且以流畅的风格进行编码并不总是正确的做法:

Fluent is not an end to itself, and coding with a fluent style is not always the right thing to do:

  • 它使创建 API 变得更加复杂,而且并不总是值得花费额外的精力。

  • It makes it more complicated to create the API, and it’s not always worth spending the extra effort.

  • 在编写代码时,由于编程语言的不惯用,有时很难使用流畅的 API。特别是,了解何时使用方法链、嵌套函数或对象作用域可能会令人困惑。

  • A fluent API is sometimes harder to use when writing code because of nonidiomatic use of the programming language. In particular, it can be confusing to know when to use method chaining or nested functions or object scoping.

  • 用作流畅样式一部分的方法的名称本身没有意义,例如Not()And()That()With()Is()

  • The methods used as part of a fluent style have names that are not meaningful on their own, like Not(), And(), That(), With(), or Is().

案例研究:以注释为指导的重构代码示例

Case Study: An Example of Refactoring Code, Guided by Comments

本案例研究从取自金融领域遗留 C# 应用程序的随机类开始:

This case study starts with a random class taken from a legacy C# application in the domain of finance:

1 公共类 Position : IEquatable<Position>
2 {
3      //可能只是 DealId
4 私有 IEnumerable<Position> 原点;
5
6      // 待定义的位置属性 ...
7 private double Quantity { 得到; 放; }
8私人双价格{得到; 放; }
9
10     // 用于调度作业的 MAGMA 属性
11 public int Id { 得到; 放; }
12 公共字符串合约类型{ get; 放; }
13 公共字符串CreationDate { 获取; 放; }
14 公共字符串ModificationVersionDate { 获取; 放; }
15 公共 bool FlagDeleted { 得到; 放; }
16 公共字符串IndexPayOffTypeCode { 获取; 放; }
17 公共字符串IndexPayOffTypeLabel { 得到; 放; }
18 公共字符串ScopeKey { 获取;放; }
19     // 结束 MAGMA 属性以分派作业
20
21 #region 构造函数
22 ...
1  public class Position : IEquatable<Position>
2  {
3     //could be just DealId
4     private IEnumerable<Position> origin;
5
6     // Position properties to be defined ...
7     private double Quantity { get; set; }
8     private double Price { get; set; }
9
10    // MAGMA properties to dispatch a job
11    public int Id { get; set; }
12    public string ContractType { get; set; }
13    public string CreationDate { get; set; }
14    public string ModificationVersionDate { get; set; }
15    public bool FlagDeleted { get; set; }
16    public string IndexPayOffTypeCode { get; set; }
17    public string IndexPayOffTypeLabel { get; set; }
18    public string ScopeKey { get; set; }
19    // end MAGMA properties to dispatch a job
20
21    #region constructors
22 ...

请注意,大多数注释都会划分部分。例如,最后一条评论用简单的英语说:“从这里到那里,这是一个仅由应用程序 MAGMA 使用的小节。” 不幸的是,简单的英语是为人服务的代码,它需要像您这样的开发人员一次又一次地处理它。

Notice that most comments delimit sections. For example, the last comment says, in plain English, “from here to there, this is a subsection that is used only by the application MAGMA.” Unfortunately, plain English is code for people, and it requires developers like you to deal with it time and time again.

您可以比这些自由文本注释更好地描述部分:您可以将它们转换为由不同类表示的正式部分。这样,您就可以将简单英语中的模糊知识转化为用编程语言表达的严格知识。下面介绍了如何在最后一部分中执行此操作:

You can do better than these free-text comments to describe sections: You can turn them into formal sections represented by distinct classes. This way, you turn the fuzzy knowledge in plain English into strict knowledge expressed in the programming language instead. Here’s how you can do this for the last section:

1 公开课 MagmaProperties
2 {
3 公共 int Id { 获取; 放; }
4 公共字符串ContractType { get; 放; }
5      公共字符串创建日期{获取; 放; }
6 公共字符串 ModificationVersionDate { 获取;放; }
7 公共 bool FlagDeleted { 得到; 放; }
8 公共字符串IndexPayOffTypeCode { 获取; 放; }
9 公共字符串IndexPayOffTypeLabel { 获取; 放; }
10 公共字符串ScopeKey { 获取;放; }
11}
1  public class MagmaProperties
2  {
3      public int Id { get; set; }
4      public string ContractType { get; set; }
5      public string CreationDate { get; set; }
6      public string ModificationVersionDate { get; set; }
7      public bool FlagDeleted { get; set; }
8      public string IndexPayOffTypeCode { get; set; }
9      public string IndexPayOffTypeLabel { get; set; }
10     public string ScopeKey { get; set; }
11 }

您可以在此处对字段的子集再次应用此方法一次或两次。例如,CreationDate并且ModificationVersionDate可能作为版本控制小节组合在一起,可以成为通用共享类:

You could apply this approach once or twice again here, on the subsets of the fields. For example, CreationDate and ModificationVersionDate probably go together as a versioning subsection that could become a generic shared class:

1 公开课 AuditTrail
2 {
3 公共字符串CreationDate { 获取;放; }
4 公共字符串ModificationVersionDate { 获取; 放; }
5 }
1  public class AuditTrail
2  {
3       public string CreationDate { get; set; }
4       public string ModificationVersionDate { get; set; }
5  }

这样做可以让你有机会更深入地思考你正在做的事情。例如,当您使用 name 时AuditTrail,很明显这应该是不可变的,以防止历史记录发生变化。

Doing this opens opportunities to think more deeply about what you’re doing. For example, when you use the name AuditTrail, it becomes obvious that this should be immutable to prevent mutation of the history.

IndexPayoffTypeCode并且IndexPayoffTypeLabel也可能在一起,正如它们相似的命名所暗示的那样:

IndexPayoffTypeCode and IndexPayoffTypeLabel also probably go together, as suggested by their similar naming:

1 索引收益类型代码
2 索引支付类型标签
1  IndexPayoffTypeCode
2  IndexPayoffTypeLabel

名称的前缀就像模块名称或命名空间。同样,这可以很好地表达为一个实际的类:

The prefix of the name acts like a module name or namespace. Again, this would be well expressed as an actual class:

1 个公共类 IndexPayoffType
2 {
3 公共字符串代码{ get; 放; }
4 公共字符串标签{ 获取; 放; }
5 }
1  public class IndexPayoffType
2  {
3       public string Code { get; set; }
4       public string Label { get; set; }
5  }

您可以继续不断地纯粹通过注释和命名来改进代码及其设计。当您执行此操作时,请使用您语言的正式语法,而不是脆弱且不明确的文本注释。

You could go on and on, improving the code and its design purely guided by comments and naming. When you do this, use the formal syntax of your languages instead of fragile and ambiguous text comments.

评论、草率的命名和其他可耻的信号都表明了改进代码的机会。如果您看到其中任何一个并且不知道替代技术,那么您需要一些外部帮助来获得干净的代码、面向对象的设计或函数式编程风格。

Comments, sloppy naming, and other shameful signals suggest opportunities for improving code. If you see any of this and don’t know the alternative techniques, you need some external help with clean code, object-orientated design, or functional programming style.

综合文档

Integrated Documentation

您的集成开发环境 (IDE) 已经满足了许多文档需求。由于自动完成功能,该文档可以更好地集成到您的编码中。这有时被称为“智能感知”,因为它能够从上下文中猜测您需要什么。当您编写代码时,IDE 会显示可用的内容。

Your integrated development environment (IDE) already fulfills many documentation needs. This documentation is even more integrated into your coding, thanks to the autocompletion. This is sometimes called “intellisense” for its ability to guess what you need from the context. As you write code, the IDE shows what’s available.

如果您写下类的名称并按句点键,IDE 会立即显示该类的每个方法的列表。但事实上,它并不是每个方法,而是经过过滤以仅显示您在光标下的代码上下文中真正可以访问的内容。例如,如果您不在类中,它不会显示私有方法。

If you write the name of a class and press the period key, instantly the IDE shows a list of every method for the class. In fact, though, it’s not every method but is filtered to show only what you can really access in the context of your code under the cursor. It doesn’t show the private method if you’re not within the class, for example.

这是一种以任务为导向并根据您的上下文精心策划的文档形式。

This is a form of documentation that is task oriented and highly curated for your context.

因此:承认您的 IDE 是用于文档目的的关键工具。了解如何很好地使用它。承认您的 IDE 处理的文档用例不必在其他任何地方处理。

Therefore: Acknowledge that your IDE is a key tool for documentation purposes. Learn how to use it well. Admit that the documentation use cases that your IDE handles don’t have to be addressed anywhere else.

类型层次结构

Type Hierarchy

类层次结构图是参考文档的经典元素。由于这些图通常使用 UML 表示法,因此它们占用了大量的屏幕空间。相比之下,您的 IDE 可以从任何选定的类中即时显示自定义类型层次结构图。该图是交互式的:您可以选择是否在所选类型上方或下方显示层次结构,并且可以展开或折叠层次结构的分支。而且因为它不使用 UML,所以它非常紧凑,因此您可以在屏幕的一小部分中看到很多内容。

A class hierarchy diagram is a classic element of a reference documentation. Because these diagrams usually use the UML notation, they take a lot of screen real estate. In contrast, your IDE can display a custom type hierarchy diagram on-the-fly from any selected class. The diagram is interactive: You select whether to display the hierarchy above or below the selected type, and you can expand or fold branches of the hierarchy. And because it’s not using UML, it’s quite compact, so you can see a lot in a fraction of the screen.

例如,如果您正在寻找具有固定长度的并发列表,但您不记得它的名称,则可以选择标准List超类型并向 IDE 询问其类型层次结构。IDE 以列表形式显示每种类型。现在,您可以按名称检查每种类型,将鼠标悬停在上面查看每种类型的 Javadoc,然后选择所需的类型。看,妈,没有文件!

If, for example, you’re looking for a concurrent list with a fixed length, but you can’t remember its name, you can select the standard List supertype and ask the IDE for its type hierarchy. The IDE displays every type that is a list. Now you can examine each type by name, have a look at the Javadoc for each by mousing over, and select the one you want. Look, Ma, no documentation!

事实上,这是文档。只是不同而已。同样,这是一种以任务为导向并针对您的上下文进行交互策划的文档形式。

Indeed, this is documentation. It’s just different. Again, this is a form of documentation that is task oriented and interactively curated for your context.

代码搜索

Code Searching

谈论 IDE 而不提及 IDE 的搜索功能是不公平的。

It would be unfair to talk about the IDE without mentioning the IDE’s searching capabilities.

当您正在寻找一个类但不记得它的名称时,您可以只键入属于该类名称的词干,区间搜索引擎将显示包含每个词干的每种类型的列表。对于仅词干的首字母缩写也同样有效。例如,您可以键入bis作为 的快捷方式BufferedInputStream

When you’re looking for a class but don’t remember its name, you can just type stems that belong to a class name, and the interval search engine will display a list of every type that contains each stem. The same works with just initials of a stem. For example, you can type bis as a shortcut for BufferedInputStream.

源自实际使用的语义

Semantics Derived from Actual Usage

阿罗拉的一位同事马蒂厄·保利(Mathieu Pauly)曾经告诉我,意义来自于事物之间的关联。因此,了解某个类含义的一种方法是查看它与您已知的所有其他类的关系。

A colleague at Arolla, Mathieu Pauly, once told me about the idea that meaning comes from the associations between things. Therefore, one way to learn what a class means is by looking at its relationships with all other classes that you already know.

从表面上看,这可能是您已经做过的事情。想象一下,您需要找到代码库中的每个事务服务。如果服务使用像这样的注释@Transactional,那么很简单:在任意位置选择注释并要求 IDE 查找所有用法。

Superficially this is something that you probably do already. Imagine that you need to find every transactional service within a code base. If the services use annotations like @Transactional, then it’s easy: Select the annotation anywhere and ask the IDE to find all usages.

或者,假设事务是通过标准 Java 类Transaction及其方法完成的Commit()。您可以要求 IDE 检索此方法的调用堆栈。每个直接或间接调用此事务内容的类都应该是事务服务。所以IDE是一个评估工具。但它远非完美。您必须将您的目标转化为 IDE 提供的功能。尽管如此,IDE 提供的所有功能仍然可以有利地取代大量原本必要的文档。IDE 是一个很棒的集成文档工具。

Alternatively, suppose that transactions are done through the standard Java class Transaction and its method Commit(). You can ask the IDE to retrieve the call stack for this method. Every class that is directly or indirectly calling this transaction stuff should be a transactional service. So the IDE is an assessment tool. It’s far from perfect, though. You have to translate your goal into what the IDE offers. Still, all the capabilities the IDE offers replace advantageously a lot of documentation that would otherwise be necessary. The IDE is a great integrated documentation tool.

您可以使用 IDE 作为用户界面,以增强代码方式扩展代码。导游和鼓舞人心的范例(参见第 5 章,“生活策展:识别权威知识”)说明了这种方法。

You can extend your code in an augmented code fashion by using your IDE as a user interface. Guided tours and inspiring exemplars (see Chapter 5, “Living Curation: Identifying Authoritative Knowledge”) illustrate this approach.

使用纯文本图表

Using Plain-Text Diagrams

大多数图表都是短暂的。图表对于特定的讨论或帮助推理特定的设计决策可能很有用,但是一旦传达了想法或做出了决定,图表就会立即失去大部分兴趣。这就是为什么餐巾草图是手工图表的最佳选择。我使用餐巾草图这个词实际上是指任何低科技的视觉和有形技术。白板CRC 卡事件风暴也具有类似的意义。它们都是以视觉方式进行交流、推理和尝试的绝佳工具。

Most diagrams are short-lived. A diagram may be useful for a particular discussion or to help reason on a specific design decision, but once the idea has been communicated or once the decision has been made, the diagram immediately loses most of its interest. This is why napkin sketches are the best choice for handmade diagrams. I use the word napkin sketches to actually refer to any low-tech visual and tangible technique. Whiteboarding, CRC cards, and event storming are of similar interest. They’re all great tools for communicating, reasoning, and trying things in a visual fashion.

有些图表确实会在较长时期内保持人们的兴趣,在这种情况下,您希望将最初的餐巾草图、一组卡片或贴纸或白板保留为更适合后代的东西。一种方法是简单地拍一张照片结果并将其存储在 wiki 中或直接存储在源代码管理中,与相关工件位于同一位置。如果图片描述的是稳定的知识,那么这种方法效果很好,但如果它描述的是定期演变的决策,那么图片在一段时间后就会产生误导。您可以尝试制作生活图,但考虑到预期的好处,这可能太难或太多工作要做。这就是您需要纯文本图表的时候。

Some diagrams do remain of interest for a longer term, and in such a case, you want to persist the initial napkin sketch, set of cards or stickers, or whiteboard into something better suited for posterity. One way is to simply take a photograph of the outcome and to store it in the wiki or directly in the source control, co-located with the related artifacts. This works fine if the picture describes stable knowledge, but if it describes decisions that evolve regularly, the picture will be misleading after a while. You could try to do a Living Diagram, but this might be too hard or too much work to do, given the expected benefits. This is when you need a plain-text diagram.

因此:将您最初的餐巾草图或一组 CRC 卡转换为纯文本形式。使用文本转图表工具自动将其渲染为可视化图表。然后,在每次更改时,维护图表的纯文本描述并将其保存在相关代码库的源代码控制区域中。

Therefore: Take your initial napkin sketch or set of CRC cards and turn it into plain-text form. Use a text-to-diagram tool to render it into a visual diagram automatically. Then, on every change, maintain the plain-text description of the diagram and keep it in the source control area in the related code base.

请记住,纯文本图表更注重内容而不是格式。您希望专注于纯文本内容,并让工具尽可能处理格式、布局和渲染。

Keep in mind that a plain-text diagram favors content over the formatting. You want to focus on the content in plain text and let the tools take care of the formatting, layout, and rendering as much as possible.

示例:纯文本图

Example: Plain-Text Diagrams

让我们考虑一下加油卡欺诈检测算法的示例。假设您在思考问题时从一张餐巾纸草图(见图8.6 )开始,列出了所需的每个相关责任以及这些责任如何互操作来解决问题。

Let’s consider the example of a fuel card fraud detection algorithm. Say that you start with a napkin sketch (see Figure 8.6) when thinking about the problem, listing every related responsibility needed and how the responsibilities interoperate to solve the problem.

下图显示了欺诈检测机制的初始餐巾草图。

图 8.6 欺诈检测机制的初始餐巾纸草图

Figure 8.6 The initial napkin sketch for a fraud detection mechanism

几天后,您的团队可能会同意您需要将餐巾草图保留为文档的一部分,并且您需要使其更易于阅读和维护,因为您期望它不时发生变化。

After a few days, your team might agree that you need to keep the napkin sketch as part of your documentation, and you need to make it easier to read and to maintain as you expect it to change from time to time.

这张图应该讲述一个故事。它应该隐藏与故事无关的所有内容。为了以故事为导向,您可以使用链接作为句子:

This diagram should tell one story. It should hide everything that does not matter for the story. To be story-oriented, you can use links as sentences:

1 <演员 A>“对”<演员 B> 做某事
1  <actor A> "does something to" <Actor B>

因此,基本上,您可以查看餐巾草图并使用以下格式的句子对其进行描述:

So, basically, you look at the napkin sketch and describe it by using sentences in this format:

1 FueldCardTransaction 接收到 FuelCardMonitoring
2 FuelCardMonitoring 查询地理编码
3 FuelCardMonitoring查询GPSTracking
4 地理编码将地址转换为AddressCoords
5 GPSTracking提供车辆坐标
6 GeoDistance 之间的车辆坐标
7 GeoDistance 之间的地址坐标
8 GeoDistance 与 DistanceThreshold 的比较
9 DistanceThreshold 交付 AnomalyReport
1  FueldCardTransaction received into FuelCardMonitoring
2  FuelCardMonitoring queries Geocoding
3  FuelCardMonitoring queries GPSTracking
4  Geocoding converts address into AddressCoordinates
5  GPSTracking provides VehicleCoordinates
6  VehicleCoordinates between GeoDistance
7  AddressCoordinates between GeoDistance
8  GeoDistance compared to DistanceThreshold
9  DistanceThreshold delivers AnomalyReport

然后你可以使用渲染工具将这组句子变成一个漂亮的图表。

Then you can turn this set of sentences into a nice diagram by using a rendering tool.

笔记

Note

对于图8.78.8所示的绘图,我使用了一个名为Diagrammr的在线工具,该工具已不再可用;然而,Zoltán Nagy 贡献了一个名为diagrammr-lite 的类似工具(在参加布达佩斯 CraftConf 的活文档研讨会时): https: //gist.github.com/abesto/a58a5e7155f38f4ac29d6c02f720a312

For the drawing shown in Figure 8.7 and 8.8, I used an online tool called Diagrammr that is not available anymore; however, Zoltán Nagy contributed a similar tool named diagrammr-lite (while attending my Living Documentation workshop at CraftConf Budapest): https://gist.github.com/abesto/a58a5e7155f38f4ac29d6c02f720a312.

渲染图的默认布局是类似活动的图,如图8.7所示。

The default layout of the rendered diagram is an activity-like diagram like the one shown in Figure 8.7.

图中显示了根据文本绘制的图表。

图8.7 文本渲染的图表

Figure 8.7 A diagram rendered from the text

但同样的文本句子也可以呈现为序列图,如图8.8所示。

But the same text sentences can also be rendered as a sequence diagram instead, as shown in Figure 8.8.

下图显示了以不同方式讲述同一故事的另一种布局。

图 8.8 以不同方式讲述同一故事的另一种布局

Figure 8.8 Another layout to tell the same story in a different way

这样的工具实际上只是 Graphviz 这样的自动布局工具之上的一个薄包装。每个句子描述了两个节点之间的关系。句子的第一个单词代表起始节点,句子的最后一个单词代表目标节点。这是一种质朴的做法。

A tool like that is in fact only a thin wrapper on top of an automatic layout tool like Graphviz. Each sentence describes a relationship between two nodes. The first word of the sentence represents the start node, and the last word of the sentence represents the target node. This is a rustic approach.

使用不同的约定来解释文本句子,创建您自己的这种方法风格并不困难。然而,重点是要保持它的质朴。如果你不保持语法简单,你最终可能会得到如此复杂的语法,以至于你必须一直查看它的语法表。

It’s not difficult to create your own flavor of this approach, using different conventions to interpret the text sentences. However, the point is to keep it really rustic. If you don’t keep the syntax simple, you may end up with syntax so complicated that you have to look at its syntax sheet all the time.

当需要对图表进行更改时,很容易在文本中进行更改。重命名可以通过查找和替换来完成。根据您的偏好,您的 IDE 可能可以将其重构自动化到达纯文本文件,在这种情况下,您忘记更新图表的风险会较小。

When there are changes that make updates to the diagram necessary, it’s easy to make them in the text. Renaming can be done through Find and Replace. Depending on your preferences, your IDE can probably have its refactoring automation reach the plain-text files, in which case you’re less at risk of forgetting to update the diagram.

图表即代码

Diagrams as Code

纯文本图的另一种风格是使用编程语言中的代码作为声明节点及其关系的方式。这种方法有以下好处:

An alternative flavor of a plain-text diagram is to use code in a programming language as the way to declare the nodes and their relationships. There are benefits to this method:

  • 您将受益于自动完成功能。

  • You get the benefit of autocompletion.

  • 编译器或解释器的检查可以捕获无效语法。

  • Checks from the compiler or interpreter can catch invalid syntax.

  • 您可以进行任何自动重构,以与所有更改保持同步。

  • You can move along with any automated refactoring to remain in sync with all changes.

  • 您可以通过编程方式从数据源生成许多动态图表。

  • You can programmatically generate many dynamic diagrams from data sources.

也有一些缺点:

There are some drawbacks, too:

  • 对于非开发人员来说,代码本身的可读性不如纯文本。

  • The code itself is less readable by nondevelopers than plain text would be.

  • 标识符的名称不能包含空格。

  • The names of identifiers cannot contain whitespace.

  • 它并不是真正的活图,而只是根据临时代码创建的图。

  • It’s not really a living diagram, but just a diagram created from ad hoc code.

下面是我的小库 DotDiagram, 11生成的图表示例,它是 Graphviz 之上的包装器:

Here is an example of a diagram generated from my little library DotDiagram,11 which is a wrapper on top of Graphviz:

11.点图, https://github.com/LivingDocumentation/dot-diagram

11.DotDiagram, https://github.com/LivingDocumentation/dot-diagram

1 最终 DotGraph 图 = new DotGraph("MyDiagram");
2 最终有向图 = graph.getDigraph();
3
4 //添加节点
5 digraph.addNode("汽车").setLabel("我的车")
         .setComment("这是一辆宝马");
6 digraph.addNode("轮子").setLabel("它的轮子")
         .setComment("我的车的轮子");
7
8
9 //添加节点之间的关联
10 digraph.addAssociation("汽车", "车轮").setLabel("4*")
          .setComment("有4个轮子")
11 .setOptions(ASSOCIATION_EDGE_STYLE);
12
13 // 渲染所有内容
14 最终字符串实际= graph.render().trim();
1 final DotGraph graph = new DotGraph("MyDiagram");
2 final Digraph = graph.getDigraph();
3
4 // Add the nodes
5 digraph.addNode("Car").setLabel("My Car")
         .setComment("This is a BMW");
6 digraph.addNode("Wheel").setLabel("Its wheels")
         .setComment("The wheels of my car");
7
8
9 // Add the associations between the nodes
10 digraph.addAssociation("Car", "Wheel").setLabel("4*")
          .setComment("There are 4 wheels")
11        .setOptions(ASSOCIATION_EDGE_STYLE);
12
13 // Render everything
14 final String actual = graph.render().trim();

根据此代码创建的图表应如图8.9所示。

The diagram created from this code should render as shown in Figure 8.9.

下图说明了“用 4 轮渲染 MyCar”。

图 8.9 渲染 MyCar —> 4* Wheel

Figure 8.9 Rendering MyCar —> 4* Wheel

当然,图表即代码的最大好处是能够从任何数据源生成图表。

Of course, the biggest benefit of diagrams as code is the ability to generate diagrams from any source of data.

概括

Summary

考虑到软件系统中的许多更改都是​​使用自动重构完成的,因此利用重构来更新文档也很有意义。在实践中,这表明对代码级技术的偏见,从纯文本到实际代码,它有助于学习许多编码技术以使代码更具表现力,例如类型的使用和仔细的命名。

Considering that many changes in a software system are done using automated refactoring, it makes a lot of sense to leverage refactorings for updating the documentation as well. In practice, this suggests a bias toward code-level techniques, from plain text to actual code, and it helps to learn many coding techniques to make the code more expressive, such as the use of types and careful naming.

第9章

Chapter 9

稳定的文档

Stable Documentation

稳定的知识很容易记录,因为它不会经常改变。稳定知识的一大好处是您可以使用任何形式的文档。因为不需要更新文档,所以即使是我本来会避免的传统形式(例如 Microsoft Word 文档或 wiki),在这种情况下也绝对没问题。然而,要做好它确实需要一些小心;您需要正确设计每个细节以确保一切都真正稳定。

Stable knowledge is easy to document because it doesn’t change often. A great benefit of stable knowledge is that you can use any form of documentation for it. Because there will be no need for updating the documents, even traditional forms that I would otherwise avoid, like Microsoft Word documents or wikis, are absolutely fine in this case. However, it does take some care to do it well; you need to properly design each detail to make sure everything is really stable.

常青内容

Evergreen Content

常青内容是指特定受众在很长一段时间内保持兴趣而不发生变化的内容。常青内容不会改变,但仍然有用、相关且准确。显然,并非每种文档都包含常青内容。

Evergreen content is a kind of content that remains of interest for a long period of time, without change, for a particular audience. Evergreen content does not change, and yet it remains useful, relevant, and accurate. Obviously, not every kind of document contains evergreen content.

常青内容文档具有以下特点:

Evergreen content documents have the following characteristics:

  • 它们往往很短,没有太多细节。

  • They tend to be short, without much detail.

  • 他们专注于高层次的知识——“大局观”。

  • They focus on high-level knowledge—“the big picture.”

  • 他们关注目标和意图而不是实施决策。

  • They focus on goals and intentions rather than implementation decisions.

  • 他们更关注商业概念而不是技术概念。

  • They focus more on business concepts than on technical ones.

这些特征对于文档的稳定性至关重要。

These characteristics are key for the stability of a document.

因此:传统的记录方式适合于很少变化的知识。当这种情况发生并且知识很有用时,就不必费心使用实时文档技术。只需将知识写入任何类型的文档中,即使是专有格式或 PDF 文档,或者使用内容管理系统、幻灯片或电子表格。但请确保从该文档中删除任何可能发生更改的内容。

Therefore: Traditional means of documentation are appropriate for knowledge that seldom changes. When this is the case and that the knowledge is useful, don’t bother with living documentation techniques. Just write the knowledge in any kind of document, even in a proprietary format or a PDF document or using a content management system, slides, or a spreadsheet. But make sure to leave out of that documentation anything that is at risk of changing.

你不必花费大量时间来创建常青的内容,但如果你这样做,它将让读者长期受益。

You don’t have to spend a lot of time on creating evergreen content, but if you do, it will benefit the readers for a long time.

请注意,知识稳定并不意味着它有用且值得记录。

Note that just because knowledge is stable doesn’t mean it’s useful and worth documenting.

需求比设计决策更稳定

Requirements Are More Stable Than Design Decisions

如果你无法改变决定,那么这就是对你的要求。如果可以的话,这就是你的设计。

If you can’t change a decision, it’s a requirement to you. If you can, it’s your design.

—阿利斯泰尔·科伯恩,https://twitter.com/TotherAlistair/status/606892091432701952

—Alistair Cockburn, https://twitter.com/TotherAlistair/status/606892091432701952

如果你无法改变一个决定,那么从你的角度来看,这个决定已经比你的设计决定更稳定了。因此,需求往往比设计决策更稳定。特别是,高级需求可能足够稳定,可以在常青文档中表达。

If you can’t change a decision, then from your perspective, that decision is already more stable than your design decisions. Hence, requirements tend to be more stable than design decisions. And in particular, high-level requirements may be stable enough to be expressed in evergreen documents.

当然,有些需求经常会发生变化,但通常是预期行为的细节发生变化。对于这些可能频繁变化的低级需求,BDD这样的实践更适合高效地应对变化;对话对于快速变化的知识非常有效,并且在适当的时候还可以进行一些自动化。

Of course, it happens that some requirements change frequently, but then it’s usually the details of the expected behaviors that change. For these low-level requirements that may change frequently, practices like BDD are more appropriate to deal with the changes efficiently; conversations are efficient for fast-changing knowledge, together with some automation when it fits.

高水平目标往往是稳定的

High-Level Goals Tend to Be Stable

一家公司可能有改变世界的愿景。这种高层次的愿景是稳定的,是公司形象的一部分。处于早期阶段的初创公司可能会定期进行转型,但其愿景通常保持不变。

A company may have a vision to change the world. Such a high-level vision is stable and is part of the company identity. A startup at an early stage may pivot regularly, but its vision often remains the same.

在大公司中,变化无处不在,但传统的管理方法是将大多数决策和知识视为确定的、可预测的和稳定的。在一个部门、团队或项目中,周围的一切通常可以被认为是稳定的。

In large corporations, change happens all the time everywhere, but the traditional approach to management is to consider most decisions and knowledge as certain, predictable, and stable. Within a department, team, or project, everything around can often be considered stable.

用几句话表达的项目愿景,就像电梯游说一样,可以非常稳定。如果情况发生变化,该项目可能会被停止或完全重新考虑。

A project vision, expressed in a few sentences, like an elevator pitch, can be quite stable. And if it ever changes, the project will probably be stopped or totally reconsidered.

为什么你的项目存在?谁赞助它?业务驱动因素是什么?预期的好处是什么?成功的标准是什么?

Why does your project exist? Who’s sponsoring it? What are the business drivers? What are the expected benefits, and what are the success criteria?

您需要格外小心,保持愿景足够高,以避免过早地限制项目的执行。

You need to take extra care to keep the vision high level enough to avoid prematurely constraining the execution of the project.

例如,项目愿景“创建一个向监管机构报告销售情况的库”已经假定了解决方案。当以这种方式表述愿景时,团队已经失去了更好的机会,例如扩展两个现有服务,以便它们一起可以交付报告。这个愿景的例子也很容易发生变化。如果新任 CTO 决定现在一切都必须是服务(不允许使用图书馆),那么团队将必须更新项目的愿景。该项目更好的愿景就是“向监管机构报告销售情况”,或者更好的是“扩展报告范围以满足 MIFID II 监管要求”。有了这样的愿景,无论你做什么来实现目标都并不重要;重要的是要实现目标。所有选项均保持开放。

For example, the project vision “Create a library to report sales to the regulator” already presumes the solution. When the vision is stated this way, the team has already lost better opportunities, such as extending two existing services so that they together can deliver the report. This example of a vision is also fragile to changes. If a new CTO decides that everything now must be services—no library allowed— the team will have to update the vision of the project. A better vision for the project would simply be “Report the sales to the regulator” or, even better, “Extend reporting to meet MIFID II regulation.” With visions like these, it does not matter what you do to achieve the goal; all options remain open.

很多知识并不像看起来那么稳定

A Lot of Knowledge Is Less Stable Than It Looks

常青文档有一个限制:即使知识本身没有太大变化,常青文档也涉及图形样式,带有公司徽标和公司特定的页脚,并且这些样式元素有时会发生变化。

There’s a limit with evergreen documents: Even if the knowledge itself does not change much, evergreen documents involve a graphical style, with a company logo and company-specific footers, and these elements of style sometimes change.

另一个限制是,所有文档(包括常绿文档)通常都与源代码驻留在同一源代码控制系统上。这鼓励轻量级文档格式,例如文本和 HTML,而不是 Microsoft Office 文档或其他二进制专有格式。以纯文本形式保存知识也是获得稳定知识的首选方式。

Another limit is that it’s common for all documents, including evergreen documents, to reside on the same source control system as the source code. This encourages lightweight formats of documents, like text and HTML rather than Microsoft Office documents or other binary proprietary formats. Keeping knowledge in plain text is also the preferred way to go for stable knowledge.

案例研究:自述文件

Case Study: A README File

作为示例,让我们考虑以下来自车队管理系统的自述文件:

As an example, let’s consider the following README file from a fleet management system:

1 # 凤凰计划
2(加油卡集成)
3
4 项目经理:安德里亚·威利夫
5
6 ## 每天同步
7 来自泵的交易数据自动发送至
8 弗莱蒂奥.
9 不再需要手动输入燃油收据或下载和
10跨系统导入燃料交易。
11
12## 加油卡交易监控
13
14 自动验证来自泵的交易数据
15 违反各种规则来检测潜在的欺诈行为:天然气
16 漏水、交易距离车辆太远等。
17 号
18
19 *负责该任务的类称为
20 燃油卡监控。*
21
22 车辆距离超过300m检测到异常
23 远离加油站,如果交易数量
24 超出车辆油箱尺寸5%以上
25
26 当驾驶员在加油站输入里程数时,Fleetio 使用该数据
27条信息触发服务提醒。这个省时省力
28 种方法可帮助您掌握维护情况并保持
29 您的车辆表现最佳。
30
31 *此模块将于 2015 年 2 月推出。请
32 请联系我们了解更多详情。*
33
34
35 ## 智能燃油管理
36 ...
1  # Project Phenix
2  (Fuel Card Integration)
3
4  Project Manager: Andrea Willeave
5
6  ## Syncs daily
7  Transaction data from the pump is automatically sent to
8  Fleetio.
9  No more manual entry of fuel receipts or downloading and
10 importing fuel transactions across systems.
11
12 ## Fuel Card Transaction Monitoring
13
14 Transaction data from the pump are verified automatically
15 against various rules to detect potential frauds: gas
16 leakage, transactions too far from the vehicle etc.
17
18
19 *The class responsible for that is called
20 FuelCardMonitoring.*
21
22 Anomalies are detected if the vehicle is further than 300m
23 away from the gas station, of if the transaction quantity
24 exceeds the vehicle tank size by more than 5%
25
26 When drivers enter mileage at the pump, Fleetio uses that
27 information to trigger service reminders. This time-saving
28 approach helps you stay on top of maintenance and keeps
29 your vehicles performing their best.
30
31 *This module is to be launched in February 2015. Please
32 contact us for more details.*
33
34
35 ## Smart fuel management
36 ...

该文件中存在许多问题,需要定期更新该文件:

There are many issues in this file that will require the file to be updated regularly:

  • 由于政治或营销原因,项目名称 Phoenix 将多次更改。

  • The project name Phenix will change many times for political or marketing reasons.

  • 项目经理的名字也可能会改变,可能每两年改变一次。

  • The name of the project manager will also likely change, likely every two years.

  • 如果团队正在进行重构,类名将在某个时候被重命名、拆分或与另一个合并,这是预期的情况。该文档每次都需要更新。

  • The class name will be renamed, split, or merged with another at some point if the team is doing refactoring, which is expected to be the case. This document will need to be updated each time.

  • 靠近类名的是具体参数,这些参数可能随时更改(例如,300m可能变为500m,并且5%可能变为3%)。

  • Close to the class name, there are concrete parameters that may change any time (for example, 300m may become 500m, and 5% may become 3%).

  • 发布日期可能会发生变化,因为它已经过去了。你如何解决这个问题?

  • The launch date is likely to change as it’s already in the past. How do you fix that?

你可以先把标题改成一个稳定的名字,参考模块的核心业务。它也可能不会永远稳定,但至少比由公司内部政治驱动的名称更稳定。为此,您需要更改以下内容:

You can start by changing the title to be a stable name, by reference to the core business of the module. It may not be stable forever either, but at least it is more stable than a name that is driven by internal company politics. To do this, you change the following:

1 # 凤凰计划
2(加油卡集成)
3
4 项目经理:安德里亚·威利夫
1  # Project Phenix
2  (Fuel Card Integration)
3
4  Project Manager: Andrea Willeave

以下标题以及简短的介绍行:

to the following title, along with a short introduction line:

1#加油卡集成
2
3 该模块的主要特点如下:
1  # Fuel Card Integration
2
3  Here are the main features of this module:

您还可以删除此文件中的项目经理姓名,因为它不是该信息的正确位置。相反,它可以位于 wiki 的团队部分,或者项目清单的团队部分(例如 Maven POM 文件)。您还可以将项目经理姓名替换为包含此信息的页面的链接。

You can also get rid of the project manager name in this file, as it is not the right place for that information. Instead, it could be in a Team section of the wiki, or in the Team section of your project manifest (a Maven POM file, for example). You could also replace the project manager name with a link to the page that contains this information.

您还应该从此文件中删除启动日期。您可以链接到公司日历、新闻门户、专门论坛或内部社交网络,或者链接到将宣布发布的 Twitter 或 Facebook 页面,而不是将其包含在此处。

You should also remove the launch date from this file. Instead of including it here, you could link to the corporate calendar, news portal, dedicated forum or internal social network, or to the Twitter or Facebook page where the launch will be announced.

类名在这里无关。如果您确实想从此文件桥接到代码,您可以链接到源代码管理上的搜索,例如“链接到标记为 的类@EntryPoint”。

The class name has nothing to do here. If you really want to bridge from this file to the code, you might instead link to a search on the source control, something like “link to the classes tagged as @EntryPoint.”

最后,这里不需要详细的参数值。如果您确实需要它们,您可以查看代码或配置,也可以检查描述预期行为以及 Cucumber 或 SpecFlow 使用的场景。

Finally, the detailed parameters values are not necessary here. If you really need them, you can either look at the code or configuration, or you can check the scenarios that describe the expected behavior and that are used by Cucumber or SpecFlow.

总而言之,代码现在如下所示:

To sum it up, here’s what the code now looks like:

1   # 凤凰计划
2   # 加油卡集成
3
4  项目经理:安德里亚·威利夫
5
6  在这里查找团队成员 // 维基百科链接
7
8
9
10该模块的主要特点如下:
11
12 ## 每天同步
13 泵的交易数据自动发送至
弗莱蒂奥.
14 不再需要手动输入燃油收据或下载和
跨系统导入燃料交易。
15
16## 加油卡交易监控
17 号
18 自动验证来自泵的交易数据
 根据各种规则来检测潜在的欺诈行为:
19 燃气泄漏、交易距离车辆太远等。
20
21
22
23 *负责此操作的类称为 FuelCardMonitoring。*
24
25 对应的代码在公司Github上 // 链接到
源代码存储库,但不是具体的类名
26
27
28 *车辆距离超过300m时检测到异常
远离加油站、 
29或交易数量超过车辆油箱尺寸
超过 5%*
30
31
32 有关欺诈检测业务规则的更多详细信息,
请在此处查看业务场景 // 链接到生活
文档
33
34
35
36##里程表读数
37 当驾驶员在加油站输入里程数时,Fleetio 使用该数据
触发服务提醒的信息。
38 这种节省时间的方法可以帮助您掌握最新情况
维护并让您的车辆保持最佳性能。
39
40
42 *此模块将于 2015 年 2 月推出。请
与我们联系了解更多详情。*
43
44
45 有关此产品的新闻和公告,请查看我们的 Facebook 页面链接至 FB 页面
46
47
48## 智能燃油管理
49 ...
1  # Project Phenix
2  # Fuel Card Integration
3
4  Project Manager: Andrea Willeave
5
6  Find who's in the team here // link to the wiki
7
8
9
10 Here are the main features of this module:
11
12 ## Syncs daily
13 Transaction data from the pump is automatically sent to
Fleetio.
14 No more manual entry of fuel receipts or downloading and
importing fuel transactions across systems.
15
16 ## Fuel Card Transaction Monitoring
17
18 Transaction data from the pump are verified automatically
 against various rules to detect potential frauds:
19 gas leakage, transactions too far from the vehicle etc.
20
21
22
23 *The class responsible for that is called FuelCardMonitoring.*
24
25 The corresponding code is on the company Github // link to the
source code repository, but not to a concrete class name
26
27
28 *Anomalies are detected if the vehicle is further than 300m
away from the gas station,
29 or if the transaction quantity exceeds the vehicle tank size
by more than 5%*
30
31
32 For more details on the business rules of the fraud detection,
please check the business scenarios here // link to the living
documentation
33
34
35
36 ## Odometer readings
37 When drivers enter mileage at the pump, Fleetio uses that
information to trigger service reminders.
38 This time-saving approach helps you stay on top of
maintenance and keeps your vehicles performing their best.
39
40
42 *This module is to be launched in February 2015. Please
contact us for more details.*
43
44
45 For news and announcements on this product, please check our Facebook page link to the FB page
46
47
48 ## Smart fuel management
49 ...

现在你有了一个常青的自述文件。

Now you have an evergreen README.

常绿文档的技巧

Tips for Evergreen Documentation

以下部分介绍了如何使文档保持最新状态的提示。

The following sections present tips on how to keep your documentation current.

避免将策略文档与其实施文档混合

Avoiding Mixing Strategy Documentation with the Documentation of Its Implementation

战略及其实施并没有同步发展。在他们的《敏捷测试:测试人员和敏捷团队实用指南》一书中,Lisa Crispin 和 Janet Gregory 建议不要将策略文档与其实现文档混合在一起,并使用测试策略的示例:

Strategy and its implementation don’t evolve at the same pace. In their book Agile Testing: A Practical Guide for Testers and Agile Teams, Lisa Crispin and Janet Gregory recommend not mixing the documentation of a strategy with the documentation of its implementation, using the example of the test strategy:

如果您的组织需要有关项目整体测试方法的文档,请考虑获取此信息并将其放入不会随时间变化太大的静态文档中。有很多信息不是特定于项目的,可以提取到测试策略或测试方法文档中。

If your organization wants documentation about your overall test approach to projects, consider taking this information and putting it in a static document that doesn’t change much over time. There is a lot of information that is not project specific and can be extracted into a Test Strategy or Test Approach document.

该文档可以用作参考,并且仅在流程发生变化时才需要更新。测试策略文档可用于让新员工对测试流程的工作方式有一个高层次的了解。1

This document can then be used as a reference and needs to be updated only if processes change. A test strategy document can be used to give new employees a high-level understanding of how your test processes work.1

1.克里斯平、丽莎和珍妮特·格雷戈里。敏捷测试:测试人员和敏捷团队的实用指南。霍博肯:艾迪生-韦斯利,2009。

1.Crispin, Lisa, and Janet Gregory. Agile Testing: A Practical Guide for Testers and Agile Teams. Hoboken: Addison-Wesley, 2009.

我在几个组织中使用这种方法取得了成功。所有项目通用的流程都被记录在一份文档中。使用这种格式可以满足大多数合规性要求。以下是已涵盖的一些主题:

I have had success with this approach at several organizations. Processes that were common to all projects were captured into one document. Using this format answered most compliance requirements. These are some of the topics that have been covered:

  • 测试实践

  • Testing practices

  • 故事测试

  • Story testing

  • 解决方案验证测试

  • Solution verification testing

  • 用户验收测试

  • User acceptance testing

  • 探索性测试

  • Exploratory testing

  • 负载和性能测试

  • Load and performance testing

  • 测试自动化

  • Test automation

  • 检测结果

  • Test results

  • 缺陷跟踪流程

  • Defect tracking process

  • 测试工具

  • Test tools

  • 测试环境

  • Test environments

因此:不要混合策略的文档和其实施的文档。使策略文档成为纯粹的常青文档。考虑到实现会更频繁地更改,请使用另一种活文档方法来实现。

Therefore: Don’t mix documentation of a strategy and documentation of its implementation. Make the strategy documentation a pure evergreen document. Use another living documentation approach for the implementation, considering that the implementation will change more frequently.

该策略应该被记录为常青文件,稳定,甚至在多个项目之间共享。从战略文件中省略所有可能更改或特定于项目的细节。所有这些变化更频繁且因项目而异的细节必须保持独立,使用本书中提出的更适合经常变化的知识的技术,例如声明性自动化和 BDD。

The strategy should be documented as an evergreen document, stable and even shared between multiple projects. Omit from the strategy document every detail that could change or that would be project specific. All these details that change more frequently and that differ from project to project must be kept separate, using the techniques proposed in this book that are more suited for knowledge that changes often, such as declarative automation and BDD.

确保稳定

Ensuring Stability

描述商业利益的名称往往是稳定的,通常持续数十年。业务正在发生变化,但从高层角度来看,仍然是销售、采购、防止损失和报告。如果您打开一本关于在您的领域开展业务的旧书,您会认识到,尽管典型的开展业务方式从那时起已经发生了变化,但书中的大多数单词仍然有效,并且仍然意味着相同的事情。业务领域词汇处于稳定的一端。

Names describing business benefits tend to be stable, often over decades. Business is changing, but from a high-level perspective, it’s still about selling, purchasing, preventing losses, and reporting. If you open an old book about doing business in your domain, you’ll recognize that although the typical way of doing business has evolved since then, most words in the book are still valid and still mean the same thing. Business domain vocabulary is on the stable end of the spectrum.

另一方面,有关组织、法律和营销的一切都是不稳定的:公司名称、子公司、品牌和商标一直在变化。避免在多个地方使用它们。更喜欢稳定的名称。

On the other end of the spectrum, everything about the organization, legal stuff, and marketing is volatile: Company names, subsidiaries, brands, and trademarks change all the time. Avoid using them in more than one place. Prefer stable names instead.

现在查看您公司的组织结构图,并将其与两三年前的组织结构图进行比较。它们有何不同?新任高管经常会改变组织结构。在一些公司中,高层管理人员每三年更换一次。部门被拆分、合并和重命名。一场永久性的业务和政治驱动的重构游戏可能会改变组织结构,而不会改变底层的业务运营。

Look at your company org chart now and compare it with one from two or three years ago. How are they different? New executives often change the org structure. In some companies the top management switches every three years. Departments are split and merged and renamed. A game of perpetual business and politics-driven refactoring may change the org structure without changing the underlying business operations much.

您是否想花时间更改代码和文档中的所有文字以适应这些更改?我当然不希望这样,所以我选择尽可能选择稳定的名称,并优先选择商业域名。

Do you want to spend time changing words everywhere in your code and in your documents to accommodate those changes? I certainly don’t want that, so I choose to go for stable names whenever I can, with a preference for business domain names.

使用常年命名

Using Perennial Naming

命名是传递知识最有力的手段之一。不幸的是,许多类型的名称经常变化,例如营销品牌和产品名称、项目代号和团队名称。发生这种情况时,维护工作会花费大量成本:必须有人找到使用旧名称的每个地方并更新每个实例。

Naming is one of the most powerful means available to transfer knowledge. Unfortunately, many kinds of names change frequently, such as marketing brands and product names, project code names, and team names. When this happens, it costs maintenance work: Somebody has to find every place where the old name is used and update each instance.

有些名字比其他名字持续时间更长,有些名字比其他名字更频繁地改变。例如,营销名称、法定名称和公司组织名称每隔一到三年更改一次是很常见的。这些名称是不稳定的。

Some names last longer than others, and some names change more frequently than others. For example, it’s common for marketing names, legal names, and company organization names to change every one to three years. These names are volatile.

明智地选择名称以使其不经常更改对于减少各种工件的维护工作量非常重要。这在代码和其他文档中都很重要。

Choosing names judiciously so that they don’t change often is important for reducing the amount of maintenance work in all kinds of artifacts. This is important in the code and also in other documents.

因此:在您维护的所有文档中使用稳定名称而不是易失名称。使用稳定的名称命名类、接口、方法、代码注释和每个文档。避免在所有文档中引用易失性名称。

Therefore: Use stable names over volatile names in all documentation that you maintain. Name classes, interfaces, methods, code comments, and every document using stable names. Avoid references to volatile names in all documents.

代码中的任意名称与描述性名称

Arbitrary Versus Descriptive Names in Code

SuperOne我注意到,不描述任何内容的任意代码名称(例如 )比描述其功能的通用名称更不稳定。即使你只在一家公司工作了两三年,你也会看到其中一些名称发生变化。但随意的名字很有吸引力,因为我们经常改变它们以适应当前的时尚。另一方面,描述事物本质的常用词(例如 )AccountingValuation很乏味,但它们不太可能被重命名,因此更稳定。更重要的是,在后一种情况下,名称本身就是文档的一个元素。无需任何其他信息,您就可以知道AccountingValuation组件的作用。

I noticed that arbitrary code names, such as SuperOne, that don’t describe anything are more volatile than common names that describe what they do. Even if you just work with a company for two or three years, you will see some of these names changing. But arbitrary names are attractive because we change them often to match the current fashion. On the other hand, common words that describe the things they are, like AccountingValuation, are dull, but they are less likely to be renamed and hence are more stable. More importantly, in the latter case, the name itself is an element of documentation. Without anything else, you may know what an AccountingValuation component does.

沿着稳定的轴组织工件

Organizing Artifacts Along Stable Axes

在宏观层面上,您如何组织文档?有许多不同的方式来组织文档:

At the macro level, how do you organize your documentation? There are many different ways to organize documents:

  • 按应用程序名称:例如CarPremiumProBestSocksOnline

  • By application name: For example, CarPremiumPro, BestSocksOnline

  • 按业务流程:例如零售卖车、网上卖袜子

  • By business process: For example, sell car in retail, sell socks online

  • 按目标客户划分:例如个人购车者、城市中产男性、B2B或B2C

  • By target client: For example, individual car buyers, urban middle-class men, B2B or B2C

  • 按团队名称:例如,团队 B2B、团队 Ninja

  • By team name: For example, team B2B, Team Ninja

  • 按团队目的:例如,巴黎软件交付、伦敦研发

  • By team purpose: For example, Software Delivery Paris, R&D London

  • 按项目名称:例如,MarketShareConquest, GoFastWeb

  • By project name: For example, MarketShareConquest, GoFastWeb

对于这些组织模式来说,随着时间的推移,它们是如何演变的?回想一下你过去的工作经历,哪些是一成不变的,哪些是时不时甚至一年几次的变化?

For each other these organization modes, how does it evolve over time? If you think back on your past work experiences, which ones remained unchanged, and which ones were changing from time to time or even several times a year?

项目开始和结束。它们被取消,有时会以新名称重新启动。应用程序的持续时间比项目长,但它们最终会被退役并被提供类似业务优势的其他项目所取代。

Projects start and end. They are canceled and sometimes resuscitated under a new name. Applications last longer than projects, but they end up being decommissioned and replaced by other projects that provide similar business benefits.

关联知识

Linked Knowledge

知识在连接时更有价值,只要连接稳定。当知识作为关系图连接起来时,它会变得更有价值,从而传达附加信息并带来结构。

Knowledge is more valuable when it is connected, provided that the connections are stable. Knowledge becomes more valuable when it is connected as a graph of relationships that conveys additional information and also brings structure.

在特定主题或项目上,所有知识都以某种方式与其他知识相关。在互联网上,资源之间的链接增加了很多价值:作者是谁?哪里可以找到更多?这个定义是什么意思呢?这里引用了谁?在书籍或论文中,参考书目会告诉您上下文。作者是否知道该出版物?如果参考书目中引用了它,那么您可以猜测情况就是如此。同样的概念也适用于您的文档。

On a particular topic or on a project, all knowledge is related to other knowledge in some way. On the Internet, links between resources add a lot of value: Who’s the author? Where can you find more? What does this definition mean? Who’s quoted here? In a book or paper, the bibliography tells you the context. Was the author aware of this publication? If it’s cited in the bibliography, then you can guess that was the case. The same concept applies to your documentation.

因此:在您的文档中,将知识与其他相关知识联系起来。确定关系的资格。定义资源标识方案,例如 URL 或引用方案。确定一种机制以确保链接长期保持稳定。

Therefore: In your documentation, link knowledge to other related knowledge. Qualify the relationship. Define a resource identification scheme, such as a URL or a citation scheme. Decide on a mechanism to ensure that the links remain stable in the long run.

使用某些元数据来限定链接非常重要,例如来源、主题参考、评论、批评、作者等。

It’s important to qualify a link with some metadata, such as the source, reference on the topic, review, criticism, author, and so on.

警告

Caution

请注意链接的方向。就像在设计中一样,链接应该从不太稳定到更稳定。

Be aware of the directions of the links. Just as in design, links should go from the less stable to the more stable.

链接到某些知识的一个好方法是通过 URL 进行访问。您可以将知识公开为可通过链接访问的网络资源。如有需要,您可以通过链接参考该知识。使用链接注册表来确保链接的持久性。

A great way to link to some piece of knowledge is to make it accessible through a URL. You can expose knowledge as web resource accessible through a link. Whenever necessary, you can refer to that knowledge by using the link. Use a link registry to ensure the permanence of the links.

许多工具通过链接公开它们的知识:问题跟踪器、静态分析工具、规划工具、博客平台和 GitHub 等社交代码存储库。如果您想链接到某些内容的特定版本,请使用永久链接。另一方面,如果您更喜欢链接到某些内容的最新版本,请链接到首页、索引或文件夹,它们通常会首先显示最新版本。

Many tools expose their knowledge through links: issue trackers, static analysis tools, planning tools, blogging platforms, and social code repositories like GitHub. If you want to link to a particular version of something, use permalinks. If, on the other hand, you prefer to link to the most recent version of something, link to the front page, or index, or folder, which will usually show the latest version first.

不稳定到稳定的依赖关系

Volatile-to-Stable Dependencies

当您引用某物时,请确保引用的方向是从较不稳定的元素到较稳定的元素。将 volatile 耦合到 stable 比将 stable 耦合到 volatile 更方便。对稳定事物的引用并不是很昂贵,因为依赖关系不会产生太多影响,因为它不会经常更改。另一方面,对于易失性依赖项的引用,只要依赖项发生变化,您就必须始终进行更改。这适用于代码和文档。

When you refer to something, make sure the direction of the reference is from the more volatile to the more stable elements. It’s much more convenient to couple the volatile to the stable than it is to couple the stable to the volatile. A reference to something stable is not very expensive as there won’t be many impacts from the dependency because it does not change often. On the other hand, with a reference to a volatile dependency, you’ll have to make changes all the time, whenever the dependency changes. This applies to both code and documentation.

对于代码中的示例,大多数编程语言建议将实现与它们实现的契约或接口耦合,而不是相反。通用的东西通常比更具体的东西更稳定。

For an example in code, most programming language propose to couple the implementation to the contract or interface they implement, and not the other way around. Generic stuff is usually more stable than more specific stuff.

在我们称之为文档的表示知识的领域中,更喜欢通过以下方式引用,而不是相反:

In the universe of representing knowledge that we call documentation, prefer references the following ways, not the other way around:

  • 从工件(代码、测试、配置、资源)到项目目标、约束和需求

  • From the artifacts (code, tests, configuration, resources) to the project goals, constraints, and requirements

  • 从目标到项目愿景

  • From the goals to the project vision

断开的链接检查器

Broken Link Checkers

如果您有到资源的直接链接,那么您需要一种方法来检测链接何时断开。当代码更改时,指向 GitHub 的代码的链接会损坏;当网站重新组织其内容或消失时,指向外部网站的链接也会损坏。

If you have a direct link to a resource, then you need a way to detect when the link is broken. Links to code at GitHub get broken when the code changes, and links to external websites get broken when the websites reorganize their content or when they disappear.

因此:使用一种机制在您的同事之前检测到损坏的链接。

Therefore: Use a mechanism to detect broken links before your colleagues do.

您可以在整个文档中使用损坏的链接检查器来检测损坏的链接。(您可以通过在线搜索“损坏的链接检查器”找到许多此类检查器。)您还可以使用低技术合同测试,当发生破坏链接的更改时,这些测试将失败。这样,您就知道何时必须修复链接或代码以使它们恢复同步。这是协调机制的另一个例子。

You can use a broken link checker on your overall documentation for detecting broken links. (You can find many such checkers by searching online for “broken link checker.”) You may also use low-tech contract tests that will fail when there is a change that breaks a link. This way, you know when you have to fix the link or the code to get them back in sync. This is another example of a reconciliation mechanism.

您可以创建一个单元测试来将可能随时更改的代码与代表外部合约(例如链接)的硬编码横向内容进行比较。当测试失败时,您知道必须更新文档或者恢复更改。

You can create a unit test to compare the code, which could change at any time, against hardcoded laterals that represent the external contract, such as the link. When the test fails, you know you have to update the doc or perhaps revert the change.

例如,如果直接在链接中使用限定类名,则契约测试可能如下所示:

For example, if the qualified class name is used directly in a link, the contract test might look like this:

1 @测试
2 公共无效 checkLinks() {
3 断言等于(
4 "flottio.fuelcardmonitoring.domain.FuelCardMonitoring",
5 FuelCardMonitoring.class.getName());
6 }
1  @Test
2  public void checkLinks() {
3  assertEquals(
4  "flottio.fuelcardmonitoring.domain.FuelCardMonitoring",
5  FuelCardMonitoring.class.getName());
6  }

每当您重构并意外违反合同时,对硬编码文字的检查将无法告诉您需要进行修复。

Whenever you refactor and accidentally break the contract, this check against the hardcoded literal would fail to tell you that you need to make a fix.

链接注册表

Link Registry

所有链接都需要维护,因为网络是有生命的,公司内部网络也是如此。当链接损坏时,您实际上并不需要遍历包含损坏链接的每个文档并将其替换为另一个链接。

All links need maintenance because the web is a living thing, and so is your internal company web. When a link is broken, you don’t really want to have to go through every document that contains the broken link and replace it with another link.

因此:不要在工件的多个位置直接包含直接链接。相反,请使用您控制下的链接注册表。

Therefore: Don’t directly include direct links in multiple places in your artifacts. Instead, use a link registry that is under your control.

链接注册表是一种间接方式,您可以更改它以在一个位置修复损坏的链接。链接注册表为您提供中间 URL 作为实际链接的别名。当链接损坏时,您只需在一个位置更新链接注册表即可重定向到另一个链接。

A link registry is an indirection that you can change to fix broken links in one single place. A link registry gives you intermediate URLs as aliases on the actual links. When a link is broken, you just need to update the link registry in one single place to redirect to another link.

内部 URL 缩短器可以完美地用作链接注册表。有些缩短器允许您选择自己的短链接;链接不仅变得更易于管理,而且也变得更短、更漂亮。

An internal URL shortener works perfectly as a link registry. Some shorteners allow to choose your own pretty short link; not only do the links become more manageable, they also get shorter and prettier.

我见过一些公司安装了自己的本地链接注册表。这对于非常关心其知识保密性的公司来说是必要的。您可以找到许多可以在本地安装的 URL 缩短程序,其中一些是开源的,另一些则具有商业许可证。

I’ve seen companies install their own on-premise link registries. This is necessary for companies that care a lot about the confidentiality of their knowledge. You can find many URL shorteners that you can install on-premise, some open source and some with commercial licenses.

添加书签的搜索

Bookmarked Searches

以更稳健的方式进行链接的另一种方式是链接到已添加书签的搜索,而不是链接到直接资源。想象一下您想要链接到ScenarioOutline存储库中的类。您可以通过直接链接进行链接。例如,在 GitHub 中,您可以使用如下链接:

Another way to link in a way that is more robust to change is to link to a bookmarked search instead of linking to a direct resource. Imagine that you want to link to the class ScenarioOutline in a repository. You could link through a direct link. For example, in GitHub you would use a link like this:

https://github.com/Arnauld/tzatziki/blob/
4d99eeb094bc1d0900d763010b0fea495a5788d\d/tzatziki-core/src/
主/java/tzatziki/analysis/step/ScenarioOutline.java
https://github.com/Arnauld/tzatziki/blob/
4d99eeb094bc1d0900d763010b0fea495a5788d\d/tzatziki-core/src/
main/java/tzatziki/analysis/step/ScenarioOutline.java

问题是这个类可能会移动到另一个包中,或者它的包可能会被重命名。类本身也可以重命名,尽管可能性不大。但任何这些更改都会将链接变成损坏的链接,这很糟糕。

The problem is that this class could move into another package, or its package might be renamed. The class itself could be renamed, too, even though that is not very likely. But any of these changes would turn a link into a broken link, and that’s bad.

因此:用更稳定标准的搜索替换直接链接。可能有多个结果,但它将帮助用户以更稳健的方式定位链接的目标。

Therefore: Replace direct links with searches on more stable criteria. There may be more than one result, but it will help users locate the target of a link in a more robust way.

您可以使用带书签的搜索而不是直接链接来使链接更可靠。例如,您可以在该特定存储库中搜索ScenarioOutline名称中包含的 Java 类。使用 GitHub 高级搜索,2您将创建以下搜索:

You can make a link more robust by using a bookmarked search instead of a direct link. For example, you could search for a Java class in this particular repository with ScenarioOutline in its name. Using the GitHub advanced search,2 you would create the following search:

2.GitHub https://help.github.com/articles/searching-code/

2.GitHub, https://help.github.com/articles/searching-code/

场景大纲位于:路径扩展名:Java 存储库:Arnauld/tzatziki
ScenarioOutline in:path extension:Java repo:Arnauld/tzatziki

其中每个选项都可以帮助创建更相关的搜索:

where each option can help create a more relevant search:

  • ScenarioOutline:搜索这个词。

  • ScenarioOutline: Search for this term.

  • in:path:搜索词必须出现在路径名中。

  • in:path: The search term must appear in the path name.

  • extension:Java:文件扩展名必须是 Java。

  • extension:Java: The file extension must be Java.

  • repo:Arnauld/tzatziki:仅在这一存储库中搜索。

  • repo:Arnauld/tzatziki: Search only in this one repo.

此搜索的结果页面将显示多个结果,但您要查找的结果很容易从列表中获取(这里是列表中的第二个结果):

The result page of this search will show more than one result, but the one you’re looking for is easy to grab from the list (here it is the second result in the list):

1 .../analysis/exec/model/ScenarioOutlineExec.java
2 .../分析/步骤/ScenarioOutline.java
3 .../pdf/emitter/ScenarioOutlineEmitter.java
4 .../analysis/exec/gson/ScenarioOutlineExecSerializer.java
5 .../pdf/model/ScenarioOutlineWithResolved.java
1  .../analysis/exec/model/ScenarioOutlineExec.java
2  .../analysis/step/ScenarioOutline.java
3  .../pdf/emitter/ScenarioOutlineEmitter.java
4  .../analysis/exec/gson/ScenarioOutlineExecSerializer.java
5  .../pdf/model/ScenarioOutlineWithResolved.java

带书签的高级搜索不仅对更强大的链接有用。一般来说,它也是活文档的重要工具。它为每个拥有浏览器的人提供了 IDE 的强大功能。通过创建策划的书签搜索,您可以创建用于导航代码和快速发现与概念相关的所有内容的导览,如此处围绕 的概念所示ScenarioOutline

A bookmarked advanced search is not just useful for more robust links. It is also an important tool for living documentation in general. It offers the power of an IDE for everyone who has a browser. By creating curated bookmarked searches, you create guided tours for navigating code and for quickly discovering everything related to a concept, as shown here around the concept of ScenarioOutline.

稳定知识的类别

Categories of Stable Knowledge

正如以下各节所讨论的,不同的知识有不同的寿命,从不稳定到长期。以下典型的稳定知识类别是常青文档的良好候选者。

As discussed in the following sections, different pieces of knowledge have different lifespans, from volatile to long term. The following typical categories of stable knowledge are good candidates for evergreen documents.

长青自述文件

Evergreen README

我们的项目的文档简短、写得不好或完全缺失。……在大量技术规范和根本没有规范之间必须有一些中间立场。事实上是有的。这个中间立场就是简陋的自述文件。

We have projects with short, badly written, or entirely missing documentation.…There must be some middle ground between reams of technical specifications and no specifications at all. And in fact there is. That middle ground is the humble Readme.

—Tom Preston-Lerner,“自述文件驱动的开发”

—Tom Preston-Lerner, “Readme Driven Development”

对于给定的 Blabla 项目,如果 README 文件专注于回答以下关键问题,则它可以安全地常绿:

For a given project Blabla, the README file can be safely evergreen if it focuses on answering the following key questions:

  • 布拉布拉是什么?

  • What is Blabla?

  • 布拉布拉如何运作?

  • How does Blabla work?

  • 谁用 Blabla?

  • Who uses Blabla?

  • 布拉布拉的目标是什么?

  • What is Blabla’s goal?

  • 组织如何从使用 Blabla 中受益?

  • How can the organization benefit from using Blabla?

  • 如何开始使用 Blabla?(但要注意:保持简单,不要经常更改。特别是,不要嵌入版本号,而是参考可以找到最新版本号的位置。)

  • How do you get started with Blabla? (But beware: Keep it so simple that it should not change often. In particular, don’t embed the version number but instead refer to the place where you can find the most recent version number.)

  • Blabla 的许可信息是什么?(这也可以在 LICENSE.txt sidecar 文件中详细说明。)

  • What is the licensing information for Blabla? (This could also be detailed in a LICENSE.txt sidecar file.)

这一级别的关键信息既重要又随着时间的推移相当稳定。

This level of key information is at the same time essential and quite stable over time.

请注意不要包含有关如何开发、使用、测试或帮助的说明以及联系信息,永久邮件列表除外。

Beware of including instructions on how to develop, use, test, or help, as well as contact information, except with permanent mailing lists.

此外,当使用 GitHub 等在线源代码存储库时,请避免从 README 链接到 wiki 上的页面:README 是有版本的,而 wiki 没有,因此链接会中断,特别是在克隆或分叉时。

Also, when using an online source code repository like GitHub, avoid linking from the README to pages on the wiki: The README is versioned, whereas the wiki is not, so links will break, in particular when cloning or forking.

愿景声明

Vision Statement

愿景是你完成工作后的世界图景。

A vision is a picture of the world as it will be when you’re done working on it.

——Twitter 上的麦卡锡秀 (@mccarthyshow)

—The McCarthy Show (@mccarthyshow) on Twitter

当经理来找我时,我不会问他:“有什么问题吗?” 我说:“告诉我这个故事。” 这样我就能找出问题的真正所在。

When a manager comes to me, I don’t ask him, “What’s the problem?” I say, “Tell me the story.” That way I find out what the problem really is.

——杂货店连锁店老板阿夫拉姆·戈德堡 (Avram Goldberg),引自《长久以来的时钟》

—Grocery store chain owner Avram Goldberg, quoted in The Clock of the Long Now

项目中的每个人都绝对应该知道的最重要的知识之一是项目或产品的愿景。

One of the single most important pieces of knowledge everybody in a project should absolutely know is the vision of the project or of the product.

有了清晰的愿景,每个团队成员的努力才能真正汇聚起来,使愿景得以实现。愿景就是梦想,但梦想同时也是对决定实现梦想的团队的行动号召。

With a clear vision, the efforts of each team member can really converge to make the vision come true. A vision is a dream, but it is a dream that is also a call to action for a team that decides to make it real.

愿景通常源自某个特定的人,他试图通过各种方式与其他人分享:

A vision often originates in a particular person, who tries to share it with other people using various means:

  • 一场演讲,讲座式的,也许有很棒的视觉效果,就像 TED 演讲一样

  • A talk, lecture-style, perhaps with great visuals, like a TED talk

  • 经常向每个人重复愿景的基调

  • Repeating the pitch of the vision often and to everyone

  • 讲述说明或例证愿景的故事

  • Telling stories that illustrate or exemplify the vision

  • 写下愿景声明

  • Writing down a vision statement

所有这些都是文档。录制在视频中的精彩演讲可能是愿景的最佳记录。

All this is documentation. A brilliant talk recorded on video may be the best documentation of the vision.

愿景必须简单,以便可以用几句话来表达。初创公司喜欢愿景陈述,但这些陈述有时缺乏深度,因为它们只是从现有的成功初创公司那里窃取。

A vision has to be simple so that it can be pitched in a few sentences. Startups love vision statements, but these statements sometimes lack depth because they just steal from existing successful startups.

愿景陈述的完美伴侣是几个能够说明愿景并使其更加真实的故事。

The perfect companion to a vision statement is a couple of stories that illustrate it and make it more real.

愿景声明通常处于稳定的一端,至少与其他项目工件(例如源代码和配置数据)相比是这样。但公司可能会不时改变其愿景,例如在转型时。

A vision statement is usually on the stable end of the spectrum, at least compared to other project artifacts, such as source code and configuration data. But it is possible that a company could change its vision from time to time, such as when pivoting.

一旦设定了愿景,就可以将其分解为高级目标。

Once the vision is set, it can be split into high-level goals.

领域愿景声明

Domain Vision Statements

一种特殊的愿景声明侧重于产品所涉及的业务领域。这个陈述的目的是描述未来系统在实际存在之前要构建的价值。该描述可能跨越多个子域,因为一开始还没有人知道域应该如何分割成子部分。领域愿景声明的重点是关注领域的关键方面。

A particular kind of vision statement focuses on the business domain the product is about. The purpose of this statement is to describe the value of the future system to be built before it actually exists. This description may span several subdomains since at the beginning no one knows yet how the domain should be split into subparts. The point of the domain vision statement is to focus on the critical aspects of the domain.

用埃里克·埃文斯的话来说:

In the words of Eric Evans:

写一篇简短的(一页)描述核心领域及其带来的价值,即“价值主张”。忽略那些无法将该域与其他域区分开来的方面。展示领域模型如何服务和平衡不同的利益。保持狭窄。尽早写下此陈述,并在获得新见解时对其进行修改。3

Write a short (1 page) description of the core domain and the value it will bring, the “value proposition.” Ignore those aspects that do not distinguish this domain from others. Show how the domain model serves and balances diverse interests. Keep it narrow. Write this statement early and revise it as you gain new insights.3

3.埃文斯,埃里克。领域驱动设计:解决软件核心的复杂性。波士顿:培生教育公司,2004 年。

3.Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. Boston: Pearson Education, Inc. 2004.

大多数技术方面和基础设施或 UI 细节不属于领域愿景声明的一部分。

Most technical aspects and infrastructure or UI details are not part of the domain vision statement.

以下是车队管理业务中加油卡监控的领域愿景声明示例:

Here is an example of a domain vision statement for fuel card monitoring in the fleet management business:

对每笔传入加油卡交易的加油卡监控有助于检测驾驶员潜在的异常行为。

Fuel card monitoring of every incoming fuel card transaction helps detect potential abnormal behavior by drivers.

通过寻找滥用模式并交叉检查来自不同来源的事实,系统报告异常情况,并由车队管理团队进行调查。

By looking for abuse patterns and by cross-checking facts from various sources, the system reports anomalies that are therefore investigated by the fleet management team.

例如,使用具有 GPS 车队跟踪功能的加油卡监控功能的客户能够发现那些偷工减料、伪造工时表、偷窃燃油或使用加油卡购买非燃油商品的员工。

For example, a client using fuel card monitoring with GPS fleet-tracking features is able to catch employees who are padding hours, falsifying timesheets, stealing fuel, or buying non-fuel goods with the fuel card.

每笔加油卡交易都会根据车辆特征及其位置历史记录进行验证,并考虑当时分配给车辆的驾驶员以及交易商家的地址。还可以计算燃油经济性来检测需要维修的发动机。

Each fuel card transaction is verified against vehicle characteristics and its location history, considering which driver was assigned to the vehicle at the time and the address of the merchant of the transaction. Fuel economy can also be calculated to detect engines in need of a repair.

领域愿景声明可作为领域主要概念的总结以及它们如何与向用户交付价值相关。它可以被视为尚未构建的实际软件的代理。

A domain vision statement is useful as a summary of the main concepts of the domain and how they are related to deliver value to the users. It can be seen as a proxy for the actual software that is not yet built.

目标

Goals

愿景是每个人都应该了解并时刻牢记的最重要的知识。从这一愿景出发,将做出许多决策以汇聚到解决方案及其实施。

The vision is the single most important piece of knowledge everybody should know and keep in mind at all times. From that vision, many decisions will be made to converge to a solution and its implementation.

仅凭愿景通常不足以让人们开始工作,您可能必须形成精确的中期目标,例如在不同团队之间分担工作或尽早探索可以做什么和替代方案。

A vision alone is often not enough for people to start working, and you may have to form precise intermediate goals, such as to share work between different teams or to explore early what could be done and the alternatives.

目标可以描述为一棵由目标和子目标组成的树,愿景位于根部。目标比愿景低,但与描述系统如何构建的所有细节相比,它们是高水平的。所以他们是属于稳定的一方,而且级别越高越稳定。

Goals can be described as a tree of goals and subgoals, with the vision at the root. Goals are lower level than the vision, but they are high level compared to all the details that describe how a system is built. They are therefore on the stable side, and the higher the level, the more stable.

大多数人都必须知道目标是长期的,而且它们很重要,因为它们驱动许多进一步的决策。因此,必须以持久的方式记录它们。由于它们也处于变化频率范围的稳定端,因此传统的文档形式适合记录目标:

Goals are also long term must be known by most people, and they are critical because they drive many further decisions. As a consequence, they must be documented in a persistent fashion. Since they are also on the stable end of the frequency-of-change spectrum, traditional forms of documentation fit for documenting goals:

  • 微软Word文档

  • Microsoft Word documents

  • 幻灯片

  • Slide decks

  • 纸质文件

  • Paper document

这并不意味着为目标制作良好的文档是很容易的。人们仍然很容易在一份因太长或太无聊而不会被阅读的文档上浪费大量时间。

This does not mean that it’s easy to make good documentation for the goals. It’s still all too easy to waste a lot of time on a document that will not be read because it’s too long or too boring.

警告

Caution

请记住,过早地决定目标是有危险的:您可能会过早地过度限制项目,而此时您对此知之甚少。这可能会阻碍项目的执行。这就是为什么 Woody Zuill 在他的博客中建议“将您的要求保持在非常高和一般的水平,直到使用前” 4,就好像它们是易腐烂的商品一样。您不想因为过早的子目标而提前拒绝机会。

Remember that there is danger in deciding goals prematurely: You may over-constrain the project too early, at a time you know very little about it. This may impede the project execution. This is why Woody Zuill advises on his blog to “keep your requirements at a very high & general level until just before use,”4 as if they were perishable goods. You do not want to reject opportunities early because of premature subgoals.

4. Woody Zuill,“生活、自由和敏捷性的追求”博客, http://zuill.us/WoodyZuill/2011/09/30/requirements-hunting-and-gathering/

4.Woody Zuill, “Life, Liberty, and the Pursuit of Agility” blog, http://zuill.us/WoodyZuill/2011/09/30/requirements-hunting-and-gathering/

影响图

Impact Mapping

探索目标和组织有关项目或业务计划的高级知识的一项伟大技术是影响图,5由 Gojko Adzic 提出。它提倡通过互动研讨会来实现目标,并将替代目标放在地图上,以便在执行过程中保持选择余地项目。这种协作技术简单且轻量级,并且涉及可视化假设和目标。

A great technique for exploring goals and organizing high-level knowledge about a project or a business initiative is impact mapping,5 proposed by Gojko Adzic. It advocates working on goals through interactive workshops and keeping the alternative goals together on a map to keep options open during the execution of the project. This collaborative technique is simple and lightweight, and it involves visualizing assumptions and targets.

5.影响图, http://www.impactmapping.org/

5.Impact mapping, http://www.impactmapping.org/

影响图显示了实现目标的选项和替代路径。因此,它不像其他传统的线性路线图那样限制执行。

An impact map shows options and alternate paths to reach a goal. It therefore does not constraint the execution as much as other traditional linear roadmaps.

影响图本身是稳定的,但建议以较低的频率重新考虑它,通常每年两次。另一方面,如果您经常发布,那么在地图上跟踪项目执行情况显然会经常发生变化,而这不应该通过每次修改地图来完成。

An impact map itself is stable, but it’s recommended to reconsider it at low frequency, typically twice a year. On the other hand, tracking the project execution on the map obviously changes often if you release often, and this should not be done by modifying the map each time.

让我们以一家音乐行业公司的影响力映射会议的结果为例,以树状思维导图的形式呈现:

Let’s take as an example the result of an impact mapping session for a company in the music industry, presented as a tree-like mind map:

1 降低歌曲版权费的处理成本
2 信息技术部门
3 100x 卷
4 加工成本降低 50%
5 销售部
6 小时统计数据
7 计费部门
8 在线实时报告(2s以内)
1  Reduce processing cost of song royalties
2  IT Department
3    100x volumes
4    50% Cheaper processing
5  Sales Department
6    Hourly stats feeds
7  Billing Department
8    Online real-time reporting (2s or less)

影响映射建议按主要利益相关者对目标进行分类,在本示例中,主要利益相关者是 IT 部门、销售部门和计费部门。它还要求在影响图中对目标进行量化,并使用成功的量化数字(称为“绩效目标”)。

Impact mapping suggests classifying the goals by main stakeholders, which would be IT department, sales department, and billing department in this example. It also requires the goals to be quantified in the impact maps, with quantitative figures of success, called “performance targets.”

还有其他类似的技术,例如 EVO 方法 Gilb, 6,用于以各种方式探索需求。

There are other similar techniques, such as the EVO method Gilb,6 for exploring requirements in various ways.

6.吉尔布, http://gilb.com

6.Gilb, http://gilb.com

无论有没有影响映射,理想的目标树都是通过墙上的便签创建的。如果您想为以后保留清晰的表示,可以使用任何思维导图应用程序(例如 MindMup、MindNode、Mindjet MindManager、Zengobi Curio 或 MindMeister)来记录和显示更清晰的思维导图布局。

With or without impact mapping, a tree of goals is ideally created with sticky notes on a wall. If you want to keep a clean representation for later, you can use any mind-mapping application, such as MindMup, MindNode, Mindjet MindManager, Zengobi Curio, or MindMeister, to record and show a cleaner layout of the map.

这些应用程序可以以各种形式读取和写入思维导图,包括缩进文本,至少作为“导入”选项。作为纯文本神器的粉丝,我最喜欢缩进文本!

These applications can read and write mind maps in various forms, including as indented text, at least as an “import” option. As a fan of plain-text artifacts, I like indented text best!

投资稳定的知识

Investing in Stable Knowledge

稳定的知识是一项可以在很长一段时间内获得回报的投资。学习一门学科是一项昂贵的投资。我很难学习半衰期只有几年的技术。

Stable knowledge is an investment that pays back over a long period of time. Learning a subject is a costly investment. I have a hard time learning technologies that have a half-life of a few years.

商业领域知识(金融、保险、会计、电子商务、制造等)是最稳定的知识之一。但由于您可能并不总是在同一领域内工作,因此您可能想知道学习特定领域的知识是否合理。但也有很多特定领域的知识可以以某种形式在其他业务领域中重用的情况。例如, Martin Fowler 所著的《分析模式》一书描述了许多取自会计或医学观察的模式,但它们几乎直接适用于金融、保险和商业。

Business domain knowledge (finance, insurance, accounting, e-commerce, manufacturing, and so on) is some of the most stable knowledge there is. But because you may not always work within the same domain, you might wonder if it’s reasonable to learn the knowledge of a particular domain. But it also happens that a lot of domain-specific knowledge is reusable in some form in other business domains. As an example, the book Analysis Patterns by Martin Fowler describes a number of patterns taken from accounting or medical observations but that work almost directly in finance, insurance, and commerce.

此外,计算基础、软件架构与设计基础也属于稳定知识范畴。不要犹豫,阅读旧论文和描绘这一领域的模式。

In addition, the fundamentals of computing and of software architecture and design also belong to the stable knowledge category. Don’t hesitate to read the old papers and the patterns that map this territory.

因此:不要犹豫,投入时间和精力去学习稳定的知识。尤其是业务领域知识和软件架构基础知识是特别值得学习的常青内容。

Therefore: Don’t hesitate to invest time and effort into learning stable knowledge. In particular, business domain knowledge and fundamentals of software architecture are evergreen content that are particularly worth learning.

领域沉浸

Domain Immersion

领域知识通常处于稳定的一面,即使您对它的理解随着时间的推移而变化(而且应该如此)。但核心用例、目的、具体示例以及与业务人员的对话大多是常青的。

Domain knowledge is typically on the stable side of the spectrum, even if your understanding of it changes over time (and it should). But the core use cases, purposes, concrete examples, and conversations with business people are mostly evergreen.

传统上,软件项目本身是学习其领域的主要方式。一个又一个的任务,每个工作部分都会带来在工作中学到的新词汇和新概念,因为它们是完成工作所必需的。这导致了许多弱点:

Traditionally, a software project itself is the main way to learn its domain. Task after task, each work part brings new vocabulary and new concepts that are learned on the job because they are necessary to do the job. This leads to a number of weaknesses:

  • 没有足够的时间来交付任务并更深入地认真研究业务领域的一部分。学习仍然是肤浅的。

  • There is not enough time to deliver a task and to study seriously a part of the business domain in more depth. Learning remains superficial.

  • 许多任务只需对底层业务有肤浅的了解就可以完成。有些事情可能看起来是巧合,但实际上却是下一个业务需求的定时炸弹。

  • Many tasks can be done with only superficial understanding of the underlying business. Something might appear to work by coincidence while really being a time bomb for next business requirements.

  • 即使您决定在任务中花两个小时来学习,当时领域专家也可能没有空。

  • Even if you decide to dedicate two hours out of the task to learn, the domain experts may not be available at that time.

每当缺乏领域知识成为瓶颈时,尽早投入时间学习该领域就是一个有吸引力的建议。做到这一点的最佳方法之一就是沉浸式体验。尽早投入时间让团队沉浸到该领域中。参观业务发生地。拍照。获取正在使用的文档的副本。仔细聆听商务人士的谈话。如果可能的话,提出问题。画出你所看到的草图并做大量笔记。

Whenever the lack of domain knowledge is a bottleneck, it’s an attractive proposition to invest time early on to learn the domain. One of the best ways to do this is through immersion. Invest time early to immerse the team into the domain. Visit the place where the business takes place. Take pictures. Get copies of the documents being used. Listen carefully to the conversations of the business people. When possible, ask questions. Make sketches of what you see and take plenty of notes.

领域沉浸也是新加入者快速发现领域内容的有效实践。它是直接来自现场的知识转移的另一种形式,这也意味着它是一种真正的文档形式。

Domain immersion is also an effective practice for new joiners to quickly discover what the domain is about. It is an alternative form of knowledge transfer, directly from the field, which also means it is a genuine form of documentation.

有时不可能去现场,或者去现场的费用太高,在这种情况下,您需要更便宜的替代方案来获取这些宝贵的知识,例如调查墙或简单的培训。

Sometimes it is not possible to go to the field, or it is prohibitively expensive to do so, in which case you need cheaper alternatives for this precious knowledge, such as an investigation wall or simple trainings.

调查墙

Investigation Wall

您可能想要创建一堵调查结果墙,就像刑事调查电影中的调查墙一样,侦探们在墙上贴满了大量图片、笔记和带图钉的地图,让自己完全沉浸在犯罪中。

You might want to create a wall of findings, much like an investigation wall in a criminal investigation movie, where the detectives cover the walls with lots of pictures, notes, and maps with pins to fully immerse themselves in the crime.

同样,您可以在墙上创建一个空间,其中包含图片、笔记、草图和示例业务文档,以便在您工作时保持对实际业务领域的感觉。

Similarly, you can create a space on a wall with pictures, notes, sketches, and sample business documents to keep a feel for the actual business domain while you work on it.

领域培训

Domain Training

团队或团队的一部分可能会受益于有关业务领域的专门培训。

A team or part of a team might benefit from specialized trainings about the business domain.

在我过去的一个项目中,当压力不是很大时,我们决定尽早投资于领域知识,所以每周两次,我们在午餐后花 30 分钟进行迷你培训。一位被确定具有某一专业领域的业务分析师或产品经理作为领域专家加入团队,一次解释我们需要了解的一个概念的所有内容(一场关于债券息票的会议,另一场关于标准金融期权的会议,另一场关于标准金融期权的会议)。新规定等)。团队认为这次培训很有用,开发人员也很喜欢。

In one of my past projects, we decided to invest in domain knowledge early, when the pressure was not very strong, so twice a week we dedicated 30 minutes after lunch to a mini-training session. A business analyst or a product manager who was identified with an area of expertise joined the team as the domain expert to explain all we needed to know on one concept at a time (a session on bond coupons, another about standard financial options, another on a new regulation, and so on). The team considered this training useful, and the developers enjoyed it.

活出我的生活课程

Live-My-Life Sessions

通过“生活我的生活”会议,一到两名开发人员在半天到两天的时间里与从事业务运营的人员保持密切联系,了解使用软件工具在业务中工作的真实情况那些人有。开发人员可能在后面,尽量不干涉,只是被动地观看。但是,他们最好能够随时或在某些预定义的暂停期间提出问题。

With “live-my-life” sessions, for a period of time from half a day to two days, one or two developers stay close to someone doing business operations to see what it’s really like to work in the business, using the software tools those people have. The developers may be in the back, trying not to interfere and just watching passively. However, it’s best for them to be able to ask questions at any time or during some predefined pauses.

这样的实验可能涉及更多。例如,开发人员可能会尝试成为业务人员的助理。有些公司更进一步,让员工在一天内完全转换角色。作为一名开发人员,做着一名会计师的工作一天可能是欣赏他们的赌注并因此改进他们的软件的最佳方式之一。它还可以为用户体验创造奇迹。

Such an experiment may be more involved. For example, a developer might try being an assistant to a business person. Some companies go further and have employees completely switch roles for a day. As a developer, doing the job of an accountant for a day could be one of the best ways to appreciate their stakes, and therefore to improve their software. It can also do wonders for the user experience.

影子用户

Shadow Users

“过我的生活”想法的一个变体是作为影子用户来观察用户的行为。开发人员以另一个真实用户的身份以只读方式登录,并实时查看该人的屏幕。观察他们如何真正使用该软件来实现其业务目标非常有价值。

A variant of the live-my-life idea is to watch the behavior of users as a shadow user. A developer logs in as another real user, in a read-only fashion, and sees that person’s screen in real time. It can be very valuable to watch how they really use the software to achieve their business objectives.

在许多情况下,拥有影子用户显然是不可行的——因为隐私原因或因为安装的软件不可访问。

Having shadow users is obviously not feasible in many cases—because of privacy reasons or because the installed software is not accessible.

长期投资

A Long-Term Investment

所有这些投资稳定知识的方式都可以被视为一种投资,因为业务领域通常相当稳定。开展业务的细节一直在变化,但业务仍然使用相同的旧概念。2007 年,当我打开一本 1992 年写的金融书籍时,我意识到了这一点。这本书的所有内容仍然具有相关性,只是例子不再现实:1992 年某些货币的利率通常在 12% 到 15% 左右,而 15 年后,这一比例接近 2%。(今天这个比例约为 0.2%!)

All these ways of investing in stable knowledge can be seen as an investment because the business domain is usually quite stable. The details of doing the business change all the time, but the business still uses the same old concepts. I realized this in 2007 when I opened a book on finance written in 1992. The book was still relevant in all its content except that the examples were no longer realistic: Interest rates in 1992 were often around 12% to 15% in some currencies, whereas 15 years later they were closer to 2%. (Today they are around 0.2%!)

即使是在计算机出现之前写的书也可能仍然很有趣。通过这些投资稳定知识的方式获得的所有背景知识将为每天的许多决策提供信息并改进。学到的所有特定领域的单词将使会议期间的讨论更加有效。您不必再在每次会议的第一部分花时间澄清词汇了。

Even books written well before the advent of computers may remain interesting. All the contextual knowledge gained through these ways of investing in stable knowledge will inform and improve many decisions every day. And all the domain-specific words learned will make discussions during meetings more efficient. You won’t have to spend the first part of each meeting clarifying vocabulary anymore.

概括

Summary

即使在变化最快的项目中,仍然有一些传统文档的空间,但仅限于稳定的知识,这些知识可以作为常青内容记录一次。本章中提供的示例只是示例,而不是规则。

Even in the most fast-changing projects there is still some room for traditional documents, but only for stable knowledge, which can be documented once as evergreen content. The examples presented in this chapter are just examples, not rules.

关注知识变化的频率是随着时间的推移减少工作量的好策略,因为这意味着您可以创建只需要手动更新几乎从不变化的知识的文档。在其他情况下,您需要使用本书其他地方描述的更动态的文档形式,并且您必须更多地依赖对话、集体工作和活文档。

Paying attention to how often pieces of knowledge change is a good strategy for reducing your workload over time because it means you can create documents that need to be manually updated only for knowledge that almost never changes. In other cases, you need to use the more dynamic forms of documentation described elsewhere in this book, and you must rely more on conversations, working collectively, and living documents instead.

第10章

Chapter 10

避免传统文档

Avoiding Traditional Documentation

我们欢迎文档,但不喜欢数百页从未维护和很少使用的大部头。#敏捷宣言

We embrace documentation, but not hundreds of pages of never-maintained and rarely-used tomes. #agilemanifesto

—推特上的@sgranese

—@sgranese on Twitter

我不知道有多少开发人员喜欢传统形式的文档。多年来我一直在收集替代方案,其中一些看起来像文档,而另一些则不是。作为第 1 章“重新思考文档”的延续本章反对文档的建立(见图10.1),并探讨了许多有助于保存和共享知识但通常不被认为是文档技术的技术。

I don’t know many developers who love traditional forms of documentation. I’ve been collecting alternatives for years, some of which look like documentation and some of which don’t. As a continuation of Chapter 1, “Rethinking Documentation,” this chapter rebels against the documentation establishment (see Figure 10.1) and explores a number of techniques that contribute to the preservation and sharing of knowledge but that are usually not thought of as documentation techniques.

图中显示了两个卡通人物说“没有文档”。

图 10.1 NODocumentation 是探索传统文档形式替代方案的宣言,其中“NO”实际上意味着“不仅”。我们承认文档的目的,但我们不同意通常的完成方式。NODocumentation 致力于探索更好的替代方案,以在人与人之间跨时间转移知识。

Figure 10.1 NODocumentation is a manifesto for exploring alternatives to traditional forms of documentation, where the “NO” actually means “Not Only.” We acknowledge the purpose of documentation, but we disagree with the way it’s usually done. NODocumentation is about exploring better alternatives for transferring knowledge between people and across time.

笔记

Note

文档只是手段,不是目的,更不是产品。

Documentation is only a means, not an end, and it is not a product.

让我们开始这一探索,认识到健康的团队一起工作并进行对话已经有效地交换了知识。

Let’s start this exploration with the recognition that healthy teams working together and having conversations already exchange knowledge efficiently.

关于正式文档的对话

Conversations About Formal Documentation

进行对话比记录对话更重要,比自动化对话更重要。

Having conversations, is more important than documenting conversations, is more important than automating conversations.

—@lunivore (Liz Kheogh)

—@lunivore (Liz Kheogh)

一个电话可以节省二十封电子邮件。一次面对面的交谈可以节省二十个电话。

A phone call can save twenty emails. A face to face chat can save twenty phone calls.

——推特上的@geoffcwatts

—@geoffcwatts on Twitter

当谈到文档时,书面文档通常是默认选择,以至于“文档”一词经常被用来表示“书面文档”。然而,当我们说我们需要文档时,我们的意思是需要将知识从某些人转移到另一些人。坏消息是,在知识传播效率方面,并非所有媒体都是平等的。

Written documentation is so often the default choice when it comes to documentation that the word documentation has often come to be used to mean “written document.” However, when we say we need documentation, we mean that there’s a need for knowledge transfer from some people to some other people. The bad news is that not all media are equal when it comes to the efficiency of transferring knowledge.

阿利斯泰尔·科伯恩 (Alistair Cockburn) 在二十年的时间里分析了三打项目。他在书籍和文章中报告了他的发现,并制作了一个著名的图表来说明不同沟通模式的有效性(见图10.2)。1

Alistair Cockburn analyzed three dozen projects over the course of two decades. He reported on his findings in books and articles, and he produced a famous diagram illustrating the effectiveness of different modes of communication (see Figure 10.2).1

图表说明了沟通的有效性随着沟通渠道的丰富而提高。

图10.2 沟通的有效性随着沟通渠道的丰富而提高

Figure 10.2 The effectiveness of communication improves with the richness of the communication channel

1.阿利斯泰尔·科伯恩。敏捷软件开发。波士顿:艾迪生-韦斯利朗文出版有限公司,2002 年。

1.Alistair Cockburn. Agile Software Development. Boston: Addison-Wesley Longman Publishing Co., Inc., 2002.

尽管图 10.2中的图表有点过时,但它回顾了阿利斯泰尔的观察,即人们在白板上一起工作和交谈是最有效的沟通方式,而纸张是最有效的沟通方式。

Although the diagram in Figure 10.2 is a bit dated, it recaps Alistair’s observation that people working and talking together at the whiteboard is the most effective mode of communication, whereas paper is the least effective.

大多数时候,有效的知识共享最好通过简单的交谈、提问和回答来完成,而不是通过书面文档。

Most of the time, effective sharing of knowledge is best done by simply talking and asking and answering questions rather than through written documents.

因此:优先考虑每个相关人员之间的对话而不是书面文件。与所有书面制品不同,对话是互动的、快速的,它们传达感情,并且具有高带宽。

Therefore: Favor conversations between everybody involved over written documents. Unlike all written artifacts, conversations are interactive and fast, they convey feelings, and they have a high bandwidth.

对话有几个重要的特征:

Conversations have several important characteristics:

  • 高带宽:与写作加阅读相比,对话提供高带宽,因为在给定的时间内可以有效地传达更多知识。

  • High bandwidth: Conversations offer high bandwidth compared to writing plus reading, as more knowledge can be efficiently communicated over a given period of time.

  • 互动:对话双方都有机会要求澄清,并在任何时候都可以就对他们最有用的话题进行培训。

  • Interactive: Both sides of the conversation have opportunities to ask for clarification and train the topic on what’s most useful for them, whenever they want to.

  • 恰到好处:谈话双方只谈论他们感兴趣的内容。

  • Just in time: Both sides of the conversation only talk about what’s of interest for them.

对话的这些关键属性使它们成为共享知识的最有效的沟通形式。

These key properties of conversations make them the most effective form of communication for sharing knowledge.

相比之下,书面文档是一种浪费,因为它需要时间来编写,也因为需要时间来定位相关部分的位置,而且内容不太可能符合预期。更糟糕的是,内容很可能会被误解。

In contrast, written documentation is wasteful because it takes time to write and also because it takes time to locate where the relevant parts are—and then it’s unlikely that the content will fit the expectations. Even worse, it’s likely that the content will be misunderstood.

Wiio的法则

Wiio’s Laws

Osmo Antero Wiio 教授创建了 Wiio 定律,这些定律以幽默的方式阐述了关于人类沟通通常会失败(除非意外)的严肃观察:2

Professor Osmo Antero Wiio created Wiio’s laws, which are serious observations formulated in a humorous way about how human communication usually fails except by accident:2

2. http://jkorpela.fi/wiio.html

2.http://jkorpela.fi/wiio.html

  • 除非意外,否则通信通常会失败。

  • Communication usually fails except by accident.

  • 如果沟通可能失败,那就一定会失败。

  • If communication can fail, it will.

  • 如果通信不会失败,那么它通常仍然会失败。

  • If communication cannot fail, it still most usually fails.

  • 如果沟通似乎以预期的方式成功,那就存在误解。

  • If communication seems to succeed in the intended way, there’s a misunderstanding.

  • 如果你对自己的信息感到满意,那么沟通肯定会失败。

  • If you are content with your message, communication certainly fails.

  • 如果一条消息可以用多种方式解释,那么它就会以一种损害最大化的方式来解释。

  • If a message can be interpreted in several ways, it will be interpreted in a manner that maximizes the damage.

人类沟通的最佳方式是互动对话,信息接收者有机会做出反应、不同意、改写或要求更多解释。这种反馈机制对于解决 Wiio 教授强调的单向人类交流的诅咒至关重要。

Human communication works best through interactive dialogue, with the opportunity for the receiver of information to react, disagree, rephrase, or ask for more explanation. This feedback mechanism is essential to fix the curse of one-way human communication highlighted by Professor Wiio.

Alistair Cockburn 在他的《敏捷软件开发》一书中表达了类似的发现:

Alistair Cockburn expresses similar findings in his book Agile Software Development:

为了使通信尽可能有效,必须提高接收者跨越始终存在的通信差距的可能性。发送者需要与接收者接触到最高水平的共享体验。两个人应该在这个过程中不断地向对方提供反馈,以便他们能够发现自己偏离意图的程度。3

To make communications as effective as possible, it is essential to improve the likelihood that the receiver can jump the communication gaps that are always present. The sender needs to touch into the highest level of shared experience with the receiver. The two people should provide constant feedback to each other in this process so that they can detect the extent to which they miss their intention.3

3.阿利斯泰尔·科伯恩。敏捷软件开发。波士顿:艾迪生-韦斯利朗文出版有限公司,2002 年。

3.Alistair Cockburn. Agile Software Development. Boston: Addison-Wesley Longman Publishing Co., Inc., 2002.

Wiio 教授强调,面对面、互动和自发的记录形式是改善沟通不畅的最佳方式。如果所有利益相关者都乐意与团队讨论所有问题和反馈,那么就不要进行任何更改。您不需要书面文档。

A face-to-face, interactive, and spontaneous form of documentation is the best way to improve on the fate of miscommunication highlighted by Professor Wiio. If all your stakeholders are happy with talking with the team for all questions and feedback, then change nothing. You don’t need written documentation.

笔记

Note

敏捷文档的目标是通过多种方式“帮助人们交互”:

The goal of agile documentation is to “help people interact” in several ways:

  • 知道该联系谁

  • Knowing who to contact

  • 了解如何开展项目、指导方针、风格和灵感

  • Knowing how to work on the project, guidelines, style, and inspirations

  • 共享相同的词汇

  • Sharing the same vocabulary

  • 共享相同的思维模型和隐喻

  • Sharing the same mental model and metaphors

  • 拥有相同的目标

  • Sharing the same goal

三种解释规则

The Rule of Three Interpretations

杰里·温伯格(Jerry Weinberg)还写了关于对收到的消息进行解释的问题,他提出了如何通过他所谓的“三种解释规则”来检查您的理解:

Jerry Weinberg also wrote about the problem of making an interpretation of a received message, and he’s proposing how to check your understanding thanks to what he calls the Rule of Three Interpretations:

如果我不能对我收到的信息想到至少三种不同的解释,那么我就没有充分考虑它可能意味着什么。4

If I can’t think of at least three different interpretations of what I received, I haven’t thought enough about what it might mean.4

4. Weinberg, Gerald M.质量软件管理第 2 卷:一阶测量。纽约:多塞特宫,1993。

4.Weinberg, Gerald M. Quality Software Management Volume 2: First-Order Measurement. New York: Dorset House, 1993.

这条规则并不能证明你的其中一个解释是预期的,但它有助于避免随机的第一个解释是正确的错觉。

This rule doesn’t prove that one of your interpretations is the one intended, but it helps avoid the illusion that the random first interpretation is the right one.

谈话的障碍

Obstacles to Conversations

如果人们在工作场所可以轻松地进行对话,那么就没有必要强调对话的重要性。不幸的是,这种情况并不常见。

There would be no need to emphasize the importance of conversations if people were easily having conversations in the workplace. Unfortunately, this is not the case often enough.

多年通过隔墙传递文件的合作方式已经训练了许多人除了会议之外不会进行任何对话,而会议中的对话通常是谈判。具有政治和信息保留的企业环境也训练同事不要过早分享太多知识,以便留在游戏中并保持权力,包括阻止权力。

Years of working together by handing documents over the wall have trained many people not to have conversations except in meetings, where conversations are often negotiations. Corporate environments with politics and information retention have also trained colleagues not to share too much knowledge too early in order to remain in the game and to keep power, including blocking power.

来自不同团队或部门的人员或者分配到不同项目或不同地点的人员往往比同一团队和项目中的近邻进行的对话要少得多。他们倾向于使用较冷淡(非互动)且效率较低的沟通方式,例如电子邮件或电话,而不是面对面的交流。值得注意的是,层级距离(即没有相同的管理)至少与地理距离一样成为对话的巨大障碍。

People from different teams or departments or people assigned on different projects or in different locations tend to have far fewer conversations than do close neighbors in the same team and project. They tend to use colder (not interactive) and less effective modes of communication, such as email or phone calls instead of face-to-face communication. It’s important to note that hierarchical distance—that is, not having the same management—is at least as great an impediment to having conversations as geographic distance.

共同努力,持续共享知识

Working Collectively for Continuous Knowledge Sharing

活动所有权的想法是另一个对话杀手:

The idea of ownership of activities is another conversation-killer:

产品“经理” 产品“所有者” Scrum “大师” 我不知道为什么人们不合作!

Product “Manager” Product “Owner” Scrum “Master” I have no idea why people aren’t collaborating!

—推特上的@lissijean

—@lissijean on Twitter

缺陷跟踪系统当然不会促进程序员和测试人员之间的沟通。他们可以很容易地避免彼此直接交谈。

Defect tracking systems certainly don’t promote communication between programmers and testers. They can make it easy to avoid talking directly to each other.

—Lisa Crispin 和 Janet Gregory,敏捷测试5

—Lisa Crispin and Janet Gregory, Agile Testing5

5.克里斯平、丽莎和珍妮特·格雷戈里。敏捷测试:测试人员和敏捷团队的实用指南。霍博肯:艾迪生-韦斯利,2009。

5.Crispin, Lisa, and Janet Gregory. Agile Testing: A Practical Guide for Testers and Agile Teams. Hoboken: Addison-Wesley, 2009.

按照不同团队(例如开发团队、QA 团队和 BA 团队)中的职能将人员分开,是减少对话可能性的好方法。陈词滥调也降低了人们想象在一起见面和交谈的可能性:

Separation of people by functions in separate teams, like the Dev, QA, and BA teams, is a great way to make conversations less likely. Old clichés also reduce the likelihood that people will even imagine meeting and talking together:

“我是测试人员,必须等开发完成才能开始测试。”

“I’m a tester, I must wait for the development to be finished to start testing.”

“我是BA,所以我必须自己解决问题,然后再交给开发人员实施。”

“I’m a BA, so I must solve the problem by myself before handing it to the developers to implement.”

“我是一名开发人员,我的工作是执行事先指定的内容,我的工作不是在完成后对其进行测试。”

“I’m a developer, my job is to execute what’s been specified beforehand, and my job is not to test it once it’s done.”

我听说一些业务分析师很难想象不生成足够大的文档,因为担心他们的工作不会被看到。他们似乎认为,仅仅通过交谈来帮助该项目可能不足以证明他们的角色是合理的。在这里,我们看到这个系统变得多么反常,产生浪费(大量的早期文档),不是为了它们本身的价值,而是为了让管理者可以看到工作。对失去工作或个人激励的恐惧助长了这种适得其反的行为。

I’ve heard that some business analysts have a hard time imagining not producing documents of a large enough size, for fear that their work will not be visible. They seem to believe that simply talking to help the project may not be enough to justify their role. Here we see how perverse this system has become, producing waste (large early documents) not for their value per se but to make the work visible to managers. Fear of losing a job or individual incentives feed this kind of counterproductive behavior.

然而,集体工作是持续分享知识的机会。确保每个人都知道唯一的目标是创造价值。为每个人创造安全的工作环境。即使文档少得多,传统的 BA 和 QA 团队成员仍然可以发挥作用,但需要将其转变为对我们称之为项目或产品的集体冒险的持续贡献。

However, working collectively is an opportunity for continuous knowledge sharing. Make sure that everybody knows that the only goal is to deliver value. Make the work environment safe for everyone. Even with far fewer documents, there’s still a role for traditional BA and QA team members, but it needs to be transformed into a continuous contribution to a collective adventure that we call a project or a product.

因此:向大家保证,经常交谈、少花时间写作是完全可以的,没有人应该为此感到内疚。提倡集体工作而不是单独的工作岗位。拥抱紧密协作能够实现持续知识共享的理念。但是,请确保少数最关键的知识以持久的方式记录在某处。

Therefore: Reassure everyone that it’s perfectly okay to have conversations often and spend less time writing, and nobody should feel guilty about that. Promote collective work over separate job posts. Embrace the idea that close collaboration enables continuous knowledge sharing. However, ensure that the few most critical pieces of knowledge are recorded in a persistent fashion somewhere.

让每个人,即使来自不同的团队,大部分时间都坐在一起,如果可能的话坐在同一张桌子旁,以便自发的沟通可以无障碍地进行。

Have everyone, even from different teams, sit close to each other most of the time, around the same table if possible, so that spontaneous communication can happen without obstacle.

对话很好。在创建软件时,我们需要进行对话,并且需要编写代码。与一位或多位同事一起同时连续地完成所有这些工作通常是一个好主意。

Conversations are good. When creating software, we need to have conversations, and we need to program code. It’s often a great idea to do all that at once, continuously, together with one or more colleagues.

集体工作有很多充分的理由,包括通过对设计的持续审查和持续讨论,为用户和维护者提高软件质量。

There are many good reasons for working collectively, including improving the quality of the software for its users and for its maintainers, thanks to continuous review and the continuous discussions on the design.

但通过频繁的对话进行集体工作也是一种特别有效的记录形式。结对编程、交叉编程、群体编程和这三个朋友完全改变了文档方面的游戏规则,因为人与人之间的知识转移是连续进行的,同时知识是在任务中创建或应用的。

But working collectively, with frequent conversations, is a particularly effective form of documentation, too. Pair programming, cross programming, mob programming and the three amigos totally change the game with respect to documentation, as knowledge transfer between people is done continuously and at the same time the knowledge is created or applied on a task.

结对编程

Pair Programming

OH:“Mob 编程。这就像‘结对编程遇上 RAID6。’”

OH: “Mob programming. It’s like ‘pair programming meets RAID6.’”

—推特上的@pcalcado

—@pcalcado on Twitter

结对编程:减少电子邮件、参加会议和编写文档的最佳方法!

Pair programming: the best way to do less email, attend fewer meetings, AND write less documentation!

—推特上的@sarahmei

—@sarahmei on Twitter

结对编程是极限编程的一项关键技术。如果代码审查很好,为什么不一直进行呢?

Pair programming is a key technique from Extreme Programming. If code reviews are good, why not do them all the time?

在结对编程中,编写代码的人(称为驱动程序)向观察者讲述正在发生的事情,观察者依次回复致谢、评论、更正和任何其他类型的反馈。观察者,也称为导航与驾驶员交谈以指导正在进行的工作,建议可能的后续步骤并表达解决任务的策略。

In pair programming, the person writing code, called the driver, narrates for the observer what’s happening, and the observer in turn replies with acknowledgements, remarks, corrections, and any other kind of feedback. The observer, also known as the navigator, talks to the driver to guide the work in progress, suggesting possible next steps and expressing strategies for solving the task.

结对工作可能不是您立即感到舒服和擅长的事情,但您可以通过实践、在工作中、在编码道场或代码静修中学习。结对编程有多种风格,例如乒乓结对,其中一个人编写一个失败的测试,然后将键盘传递给另一个人以使其通过并重构。

Working in pairs may not be something you are comfortable with and good at immediately, but it’s something you can learn through practice, on the job or in coding dojos or on code retreats. There are various styles of pair programming, such as ping-pong pairing, where one person in the pair writes a failing test and then passes the keyboard for the other to make it pass and refactor.

为了尽可能多地共享知识以拥有真正的集体所有权,在结对编程中,通常会定期更换特定任务中的合作伙伴。根据团队的不同,这种配对轮换的频率可能每小时或每天发生一次,也可能每周发生一次。有些团队没有固定的频率,但要求任何任务不能由开始任务的两人完成。

For sharing knowledge as much as possible in order to have true collective ownership, in pair programming it’s common to regularly change the partners in the pairs on a given task. Depending on the teams, this rotation of pairs can happen as frequently as every hour, or every day, or it may happen just once a week. Some teams don’t have a fixed frequency but require that any task cannot be finished by the pair who started it.

交叉编程

Cross Programming

交叉编程是结对编程的一种变体,其中观察者不是开发人员而是业务专家。每当编程任务需要对业务领域有深入的了解时,这就是一种高效且非常有效的协作形式,因为两人在计算机前做出的所有决策都与业务更相关。这个名字是我的同事 Houssam Fakih 创造的,他在会议上谈到了这种方法。6

Cross programming is a variant of pair programming where the observer is not a developer but a business expert. Whenever the programming task requires a deep understanding of the business domain, it’s a form of collaboration that is highly efficient but also very effective as all decisions taken by the pair in front of the computer are more relevant to the business. The name was coined by my colleague Houssam Fakih, who talked about this approach in conferences.6

6. @Houssam, https://speakerdeck.com/fakih/cross-programming-forging-the-future-of-programming

6.@Houssam, https://speakerdeck.com/fakih/cross-programming-forging-the-future-of-programming

群体编程

Mob Programming

Mob 编程是一种软件开发方法,整个团队在同一时间、同一空间、同一台计算机上处​​理同一件事。这类似于结对编程,即两个人坐在同一台计算机上,同时协作处理相同的代码。通过 Mob 编程,协作可以扩展到团队中的每个人,同时仍然使用一台计算机来编写代码并将其输入到代码库中。

Mob programming is a software development approach where the whole team works on the same thing, at the same time, in the same space, and at the same computer. This is similar to pair programming where two people sit at the same computer and collaborate on the same code at the same time. With Mob Programming the collaboration is extended to everyone on the team, while still using a single computer for writing the code and inputting it into the code base.

—mobprogramming.org

—mobprogramming.org

所有才华横溢的人在同一时间、同一空间、同一台计算机上、同一件事上工作。

All the brilliant people working at the same time, in the same space, at the same computer, on the same thing.

——伍迪·祖尔7

—Woody Zuill7

7.祖伊尔,伍迪。“Mob 编程——整体团队方法”, https://www.agilealliance.org/wpcontent/uploads/2015/12/ExperienceReport.2014.Zuill_.pdf

7.Zuill, Woody. “Mob Programming–A Whole Team Approach,” https://www.agilealliance.org/wpcontent/uploads/2015/12/ExperienceReport.2014.Zuill_.pdf

Mob 编程是集体编程形式的最新成员,并迅速流行起来。如果极限编程将代码审查旋钮调至 10,那么暴徒编程就会更进一步,将其调至 11。

Mob programming is a recent addition to the stable of collective forms of programming and has quickly gained popularity. If Extreme Programming turned the code review knob to 10, mob programming goes even further, turning it to 11.

在群体编程中,不存在结对轮换的问题,因为每个人总是参与任何任务,所以每个人都知道每个任务。这就是名副其实的集体所有权——同一时间同一地点。

In mob programming, there is no question of pair rotation as everybody’s always present on any task, so everybody knows about every task. That’s literally collective ownership—in the same place at the same time.

在一个由五人组成的全职进行 Mob 编程的团队中,知识共享不是问题,因为它每秒都在持续进行。每当有人必须去外面开会时,团队的其他成员都会继续工作,几乎不受影响。

In a team of five people doing mob programming full time, knowledge sharing is not an issue, as it’s done continuously, every second. Whenever someone has to attend a meeting outside, the rest of the team keeps on working, almost unaffected.

三个朋友(或更多)

The Three Amigos (or More)

产品负责人、开发人员和测试人员坐下来讨论正在开发的系统应该做什么。产品负责人描述用户故事。开发人员和测试人员提出问题(并提出建议),直到他们认为自己可以回答基本问题:“我如何知道这个故事已经完成?”

A Product Owner, a Developer, and a Tester sit down to talk about something that the system under development should do. The Product Owner describes the user story. The Developer and Tester ask questions (and make suggestions) until they think they can answer the basic question, “How will I know that this story has been accomplished?”

无论如何或何时完成,这三个朋友(借用我在 Nationwide 的朋友的术语)必须就这个基本标准达成一致,否则事情就会出错。

No matter how or when it’s done, these three amigos (to borrow a term from my friends at Nationwide) must agree on this basic criteria or things will go wrong.

—乔治·丁维迪,http://blog.gdinwiddie.com/2009/06/17/if-you-dont-automate-acceptance-tests/

—George Dinwiddie, http://blog.gdinwiddie.com/2009/06/17/if-you-dont-automate-acceptance-tests/

三个朋友在规范研讨会期间一起工作的概念是 BDD 方法的核心。与结对编程、交叉编程和群体编程相比,这三个朋友不是在代码上工作,而是在描述要构建的软件的预期业务行为的具体场景。尽管如此,每个参与的人都拥有这些场景,并且谁将它们写在纸上或测试自动化工具(例如 Cucumber)中并不重要。尽管常见术语是“三个朋友”,但在实践中,只要另一个视角(例如,用户体验、运营)是工作成功的关键,就可能有超过三个朋友。

The concept of the three amigos working together during specification workshops is central to the BDD approach. In contrast with pair programming, cross programming, and mob programming, the three amigos are not working on code but on concrete scenarios describing the expected business behavior of the software to build. Still, everyone involved owns the scenarios, and it does not matter who writes them down on paper or in a test automation tool (such as Cucumber). Although the common term is “three amigos,” in practice there may be more than three whenever another perspective (for example, UX, Ops) is key for the success of the work.

事件风暴作为入职流程

Event Storming as an Onboarding Process

Alberto Brandolini 发明了“事件风暴” 8 ,这是一种使用大墙上的贴纸进行协作建模的活动。他说,一些团队发现每当有新成员加入团队时就运行新的事件风暴会议很有价值,作为一种快速入职机制。我可以证明事件风暴对此非常有效。作为一名顾问,总是在新领域的新团队中呆上几天,我需要在短时间内尽可能多地学习新领域。最近我用过为此,即使团队之前已经做过几次,也可以进行简短的事件风暴会议。通过这样的研讨会,您在短短两个小时内就能学到很多东西,真是令人印象深刻。

Alberto Brandolini invented Event Storming,8 a collaborative modeling activity using sticker notes on a large wall. He says that some teams find it valuable to run a new event storming session whenever a new member joins the team, as a fast onboarding mechanism. I can testify that event storming works very well for that. As a consultant spending just a few days always in new teams with new domains, I need to learn as much as possible of the new domain in a short period of time. Recently I’ve used short event storming sessions for that, even if the team has done it several times before. It is really impressive how much you can learn in just two hours with this kind of workshop.

8. https://www.eventstorming.com/

8.https://www.eventstorming.com/

最近,一位业务领域专家在事件风暴会议上表示,他已经在该领域创建了精心设计的图表。当我们将事件张贴在墙上并进行组织时,他在白板上画了图表。有趣的是,他的图表在很多方面都比我们的事件墙更完整。尽管如此,交互式研讨会的形式意味着我们比通常只看静态图表时更加关注贴纸墙。这次会议变成了一个比较图表和事件墙的游戏,以更好地理解两者,并且在这个过程中出现了很多新的见解。

It happened recently that a business domain expert in an event storming session said that he had already created well-crafted diagrams on the domain. When we were mostly done with posting the events on the wall and organizing them, he drew the diagram on the whiteboard. It was interesting that his diagram was in many ways more complete than our wall of events. Still, the interactive workshop form meant that we were all much more engaged with our wall of stickers than is typically the case when just looking at a static diagram. The session became a game of comparing the diagram and the events wall to better understand both, and a lot of new insights appeared in this process.

知识转移会议

Knowledge Transfer Sessions

知识转移 (KT) 会议在不倾向于进行结对编程或群体编程的公司中很常见。团队将 KT 计划作为其计划工作的一部分,通常除了创建简短的文档之外,以确保知识得到真正的共享和良好的理解。根据 Wiio 的法律,这是一个好主意。KT 的一个典型示例是在发布之前交换有关部署的知识,此时运维人员位于组织的另一个孤岛中。在这种情况下,共享知识的一种方法是根据部署文档和所有自动化部署清单执行部署的试运行。这样,任何问题、问题或错误都可以在会议期间快速发现,而所有这些都发生在正常工作时间内。

Knowledge transfer (KT) sessions are common in companies that don’t tend to do pair programming or mob programming. The teams plan KT as part of their scheduled work, often in addition to creating brief documents, to make sure the knowledge is actually shared and well understood. According to Wiio’s laws, that is a good idea. A typical example of KT would be to exchange the knowledge on the deployment before a release, when the Ops are in another silo of the organization. One way to share knowledge in this case is to perform a dry run of the deployment, based on the deployment document and all the automated deployment manifests. This way, any issue, question, or mistake can be spotted quickly during the session—and all this happens during regular working hours.

当然,另一种选择是开发人员和运维人员直接集体工作,准备、配置和记录所有部署过程。对于传统公司来说,KT 可以朝这个方向迈出一步,就像代码审查是迈向结对编程的一步一样。

Of course, an alternative is to directly work collectively between the developers and the Ops people to prepare, configure, and document all the deployment process. KT can be a step in this direction for traditional companies, just as code reviews are a step toward pair programming.

持续记录

Continuous Documentation

集体工作形式最适合连续记录。面对面的交互式对话是最有效的沟通形式,结对编程、交叉编程、三友编程和暴民编程精确地组织工作,以最大限度地提高有效对话的机会。文档记录是在需要知识的时候发生的。所有必须了解此事的人都在场,可以立即提出问题以澄清要点。

Collective forms of work are optimal for continuous documentation. Face-to-face interactive conversations are the most efficient form of communication, and pair programming, cross programming, the three amigos, and mob programming organize the work precisely to maximize the opportunities for effective conversations. Documentation happens at the very time the knowledge is necessary. Everyone who must know about it is present and can immediately ask questions to clarify points.

当任务完成后,相关人员会记住知识的一些关键部分,而忘记其余的部分。如果某人去度假,这些知识在他或她的同事的头脑中是安全的,因此某人的缺席不会妨碍正在进行的工作。

When the task is done, those involved remember some of the key parts of knowledge and can forget the rest. If someone goes on vacation, the knowledge is safe in his or her colleagues’ mind, so someone’s absence does not impede the work in progress.

卡车因素

Truck Factor

集体工作对于提高项目的卡车系数非常有好处,即在项目陷入严重麻烦之前团队中必须被卡车撞到的人数。卡车系数衡量单个团队成员的信息集中度。卡车系数为 1 意味着只有一个人知道系统的关键部分,如果该人不在,就很难恢复这些知识。

Working collectively is very good for improving the truck factor of a project—that is, the number of people on a team who have to be hit with a truck before the project is in serious trouble. The truck factor measures the concentration of information in individual team members. A truck factor of one means that only one person knows critical parts of the system, and if that person is not available, it would be hard to recover the knowledge.

当多个团队成员在项目的每个部分进行协作时,知识自然会被更多人复制。当他们离开、去度假或只是去开会时,工作可以在没有他们的情况下继续进行。

When several team members collaborate on every part of a project, knowledge is naturally replicated in more people. When they leave or go on vacations or just leave for a meeting, the work can carry on without them.

小卡车因素通常意味着某人是项目中的英雄,拥有许多未与其他队友分享的知识。这对于项目的弹性来说绝对是一个问题,管理层应该意识到这一点。引入集体编程形式是减轻此类风险的好方法。将英雄转移到附近的另一支队伍是另一种处理方法。

A small truck factor usually means someone is a hero on the project, with a lot of knowledge not shared with other teammates. This is definitely a problem for the resilience of the project, and management should be aware of it. Introducing collective forms of programming is a nice way to mitigate such risk. Moving the hero to another team nearby is another way to deal with it.

咖啡机通讯

Coffee Machine Communication

并非所有的知识交流都需要计划和管理。在轻松的环境中进行自发讨论通常效果更好,必须予以鼓励。

Not all exchange of knowledge has to be planned and managed. Spontaneous discussions in a relaxed environment often work better and must be encouraged.

在咖啡机或饮水机旁随机讨论是非常有价值的。有时最好的知识交流是自发的。您遇到一两个同事并开始交谈。然后,您可以进行内容协商之类的活动,以找到每个人都感兴趣的主题。您可能会涉及到非专业主题。在这种情况下,您正在建立一种无价的纽带。当您选择一个专业主题时,您就找到了最佳的沟通方式:您选择这个主题是因为大家都对此感兴趣。您对当前的任务有疑问,其他人很乐意帮助您提供答案或根据自己的经历讲述故事。

Random discussions at the coffee machine or at the water fountain are invaluable. Sometimes the best exchange of knowledge is spontaneous. You meet a colleague or two and start talking. Then you have something like a content negotiation to find topics each of you is interested in. You may land on a nonprofessional topic. In this case, you are creating a bond, which is invaluable. When you choose a professional topic, you’ve landed on the best type of communication: You’ve chosen this topic because all of you have an interest in it. You have questions about your current tasks, and the other people are happy to help with answers or stories from their own experience.

我相信这种沟通是交流知识的最好方式。该主题是根据共同兴趣自由选择的。它是互动的,有问题和答案,还有很多自发的故事讲述。只要需要就可以。我有时会错过会议,因为咖啡机上的讨论对于项目来说比我应该参加的会议更重要。

I believe that this kind of communication is the very best way to exchange knowledge. The topic is chosen freely based on shared interests. It’s interactive, with questions and answers and a lot of spontaneous storytelling. It takes as long as required. I’ve sometimes missed meetings because the discussion at the coffee machine was way more essential to a project than the meeting I was supposed to attend.

用于聚会和非会议的开放空间技术恰好为较大的团体复制了这种类型的想法设置。两英尺法则规定,每个人都可以自由地移动到最有趣的话题。其他重要原则是“那里的人都是正确的人”以及“无论何时开始,都是正确的时间”。

Open space technology used for meet-ups and un-conferences replicates just this type of idea setting for larger groups. The Law of Two Feet states that everyone is free to move where the topic is most interesting. Other important principles are that “The people who are there are the right persons” and that “Whenever it starts it’s the right time.”

为了使这种类型的沟通发挥作用,咖啡机周围必须不存在等级压力。每个人都必须能够自由地与首席执行官交谈,而不是正式或害羞。

For this type of communication to work, there must be no hierarchy pressure around the coffee machine. Everybody must be free to chat with the CEO without being formal or shy.

因此:不要低估在咖啡机、饮水机或休闲区随机讨论的价值。为每个人创造在轻松的环境中随机见面和交谈的机会。规定在所有轻松的对话中必须忽略等级制度中的等级。

Therefore: Don’t discount the value of random discussions at the coffee machine or water fountain or in the relaxation area. Create opportunities for everyone to meet and talk at random in a relaxed setting. Decree that the rank in the hierarchy must be ignored within all relaxed conversations.

谷歌和其他网络初创公司提供了出色的设施来鼓励人们见面和交谈。只要问问杰夫·迪恩(Jeff Dean)即可,他是著名的谷歌员工,经常被称为互联网的查克·诺里斯(Chuck Norris)。作为第 20 位 Google 员工,Dean 取得了一系列令人印象深刻的成就,包括带头设计和实施广告服务系统。Dean 在陌生的深度学习领域取得了巨大的成就,突破了极限,但如果不主动与同事一起喝到 20,000 杯卡布奇诺,他就不可能做到这一点。“我对神经网络了解不多,但我确实对分布式系统了解很多,我只是走到厨房或任何地方的人跟他们交谈,”迪恩告诉 Slate。9

Google and other web startups propose fantastic facilities to encourage people to meet and talk. Just ask Jeff Dean, the famed Googler who often is referred to as the Chuck Norris of the Internet. As the 20th Googler, Dean has a laundry list of impressive achievements, including spearheading the design and implementation of the advertising serving system. Dean pushed limits by achieving great heights in the unfamiliar domain of deep learning, but he couldn’t have done it without proactively getting a collective total of 20,000 cappuccinos with his colleagues. “I didn’t know much about neural networks, but I did know a lot about distributed systems, and I just went up to people in the kitchen or wherever and talked to them,” Dean told Slate. “You find you can learn really quickly and solve a lot of big problems just by talking to other experts and working together.”9

9. Tech Crunch, http://techcrunch.com/2015/09/11/legendary-productivity-and-the-fear-of-modern-programming/

9.Tech Crunch, http://techcrunch.com/2015/09/11/legendary-productivity-and-the-fear-of-modern-programming/

La Gaité Lyrique 是巴黎的一个致力于数字艺术的文化场所,设有办公室和会议室,但在那里工作的工作人员通常更喜欢在向公众开放的门厅举办会议(见图 10.3 。他们甚至在那里提供啤酒,但我没有看到工作人员白天喝啤酒。

La Gaité Lyrique, a cultural venue in Paris devoted to digital arts, has offices and meeting rooms, but the staff working there often prefer to host meetings in the foyers that are open to the public (see Figure 10.3). They even serve beer there, but I haven’t seen people from the staff drink beer during the day.

图中显示了大多数会议举行的非正式门厅之一。

图 10.3 大多数会议举行的非正式门厅之一

Figure 10.3 One of the informal foyers where most meetings take place

我在他们的门厅里花了无数个小时来写这本书。我体验到了在封闭式会议室的传统工作环境中所无法获得的好处:

I’ve spent countless hours in their foyers writing this book. I’ve experienced benefits that would be missed in a traditional work environments with closed meeting rooms:

  • 气氛:因为外面的人混杂在一起,很多人在工作,其他人围着茶或啤酒玩,气氛很轻松。这是令人愉快的,它鼓励更具创造性的思考。您可以选择矮沙发和躺椅或带厨房椅子的餐桌。谈到紧张的话题,我每次都会去休息室!为了绘制图表,我会选择餐桌。

  • The atmosphere: Because there is a mix of people from the outside, many working and other having fun around tea or beer, the atmosphere is quite relaxed. This is pleasant, and it encourages thinking more creatively. You have the choice of low sofas and lounge chairs or dining tables with kitchen chairs. On a tense topic, I’d go for the lounge setting each time! To work on a diagram, I’d choose the dining table.

  • 即兴讨论:例如,La Gaité Lyrique 的总经理与两名员工会面。他们没有预订空间。谈话结束后,总经理环顾四周看看谁在场,然后与在门厅参加另一场会议的同事进行了非常简短的私下讨论。

  • Impromptu discussions: For example, the general director of La Gaité Lyrique had a meeting with two people from the staff. They didn’t book a space. When that conversation was done, the general director looked around to see who was there and went on to have very brief side discussions with colleagues who were attending another meeting in the foyer.

回想起在企业界、在无聊的会议室中与忙碌的客户计划会议的所有挫败感,我很嫉妒 La Gaite Lyrique 的员工拥有如此更好的协作体验。

Thinking back on all the frustrations of planning meetings with busy clients in the corporate world, in boring meeting rooms, I was jealous that the staff working at La Gaite Lyrique had such a better collaboration experience.

和工作人员在一起也意味着我有机会以即兴的方式向场馆总监本人提问——无需预约,也没有秘书来过滤访问权限。哇。

Being there with the staff also means I had the opportunity to ask questions to the director of the venue himself in an impromptu fashion—no appointment, no secretary to filter access. Wow.

主管告诉我,他绝对鼓励非正式会议。在门厅度过休闲时间而不是工作不是问题,因为每个人都有自己的责任,无论他们如何、何时、何地工作或工作多长时间。即兴会议可以在非正式空间(例如咖啡机区)完全即兴或计划。

The director told me he definitely encourages informal meetings. Spending leisure time in the foyer instead of working is not a problem because everyone owns their responsibilities, regardless of how, when, where, or how long they work. Impromptu meetings can be totally improvised or planned in an informal space, like in the coffee machine area.

当然,咖啡机通信并不适合所有情况。无法保证您会在咖啡机周围找到您想交谈的人除非你计划会议。也没有活动挂图,没有白板,不幸的是没有电话会议系统。而且没有隐私。

Coffee machine communication is not suited for every case, of course. There is no guarantee that you’ll find the people you want to talk to around the coffee machine unless you plan the meeting. There’s also no flip chart, no whiteboard, and unfortunately no teleconferencing system. And there is no privacy.

笔记

Note

对话、集体工作和自发知识共享的场所代表了大多数知识的理想文档形式。然而,当所有团队成员都离开或忘记了遥远过去的知识时,这种方法无法扩展到大量人员,并且不足以获取长期必需的知识。对于很多人感兴趣的知识来说这还不够,对于太关键而不能留在口头上的知识来说这也是不够的。有时你需要更多的东西,以及一种从非正式逐渐演变为更正式的方法。

Conversations, working collectively and places for spontaneous knowledge sharing represent the ideal form of documentation for most knowledge. However, this approach doesn’t scale to large groups of people and is not enough for knowledge that is essential in the long term, when all team members are gone or have forgotten knowledge from the remote past. It’s not enough for knowledge that is of interest to a large number of people, and it’s not enough for knowledge that’s too critical to be left as spoken words. Sometimes you need something more, and a way to gradually evolve from the informal to the more formal.

理念沉淀

Idea Sedimentation

“记忆是思想的残留物。” - 简单但深刻的认识对我的工作非常重要。我打算更充分地尊重它。

“Memory is the residue of thought.” - simple but profound realization that is so important to my work. I intend to honor it more fully.

——推特上的@tottinge

—@tottinge on Twitter

确定某条知识是否重要可能需要时间。许多知识只有在被创造的那一刻才重要。您可能会争论设计选项,尝试一种,发现它不对,然后尝试另一种。一段时间后,很明显这是正确的选择,并且该选择在代码中可见。它已经在那里了。无需再做任何事情。

It can take time to find out whether a piece of knowledge was important or not. A lot of knowledge is important only at the moment it’s created. You might debate design options, try one, find out it’s not right, and try another. After some time, it may be obvious that it was the right choice, and the choice is visible in the code. It’s already there. There is no need to do anything more.

你们讨论咖啡机周围的选项。你在心里模拟他们的表现。每个人都同意最好的选择。然后两人回到他们的计算机前实施它。讨论期间交流和创造的知识在那个特定时期非常重要。但第二天,就已经只是一个细节了。

You discuss options around the coffee machine. You mentally simulate how they will perform. Everybody agrees on the best option. Then a pair goes back to their computer to implement it. The knowledge exchanged and created during the discussion was important at that particular time. But the next day, it’s already nothing more than a mere detail.

有时,某些知识即使在一段时间后仍然很重要。它会得到强化,直到值得录制并与更多观众和未来分享。

Once in a while, some of this knowledge remains important even after a while. It gets reinforced until it’s worth recording to be shared with a larger audience and for the future.

因此:在一小群人中,倾向于快速、快速、廉价的互动知识交流方式,例如对话、草图和便利贴。仅将那些已被反复证明有用、至关重要或每个人都应该知道的知识(尤其是在更大范围内)推广到更重量级的文档形式。

Therefore: Within small groups of people, favor quick, fast, cheap interactive means of knowledge exchange such as conversations, sketching, and sticky notes. Only promote to more heavyweight forms of documentation the fraction of knowledge that has repeatedly proven to be useful, that is critical, or that everybody should know, especially at a larger scale.

从即兴对话开始,然后将关键部分变成永久性的东西,无论是增强代码、常青内容还是任何持久的东西。

Start with impromptu conversations and later turn the key bits into something permanent, whether it be augmented code, evergreen content, or anything durable.

现场交换的知识可以通过智能手机拍摄的照片、手写笔记等记录下来(见图10.4)。但这些形式的文档随后往往会被忽略。

The knowledge exchanged live can be captured as traces, through a photograph taken with your smartphone, handwritten notes, and so on (see Figure 10.4). But these forms of documentation will often be ignored later on.

一张图说明了对话的痕迹。 该图显示了三个人的卡通形象,分别提出对话、辩论和决定。 显示将对话、辩论和决策转换为照片或笔记。

图 10.4 与踪迹的对话

Figure 10.4 Conversations to traces

沉积比喻将流动的想法与溪流中流动的沙子联系起来。沙粒快速移动,但其中一些在溪流底部变成沉积物,并在那里缓慢积累。葡萄酒醒酒器中也有类似的过程(见图10.5)。

The sedimentation metaphor relates ideas that are flowing to sand flowing in a stream. The sand particles move along quickly, but some of them become sediment at the bottom of the stream, where they accumulate slowly. A similar process is at work in a wine decanter (see Figure 10.5).

下图显示了颗粒沉降到醒酒器底部的情况。

图 10.5 颗粒沉降到醒酒器底部

Figure 10.5 Particles settling to the bottom of a wine decanter

制作一张餐巾草图来记录设计方面,然后如果证明有必要,则将其变成可维护的东西,例如纯文本图或活图或可见测试。

Make a napkin sketch to document a design aspect, and later if it proves essential, turn it into something maintainable, like a plain-text diagram or a living diagram or a visible test.

使用项目符号点来记录质量属性,然后如果没有太大变化,将项目符号转变为可执行的场景。

Use bullet points to document the quality attributes, and later if it hasn’t changed much, turn the bullets into executable scenarios.

废弃文档

Throw-Away Documentation

有些文档仅在有限的时间内有用,然后可以删除。例如,当您围绕问题进行设计时,您需要一个特定的图表。当您解决完问题后,图表立即失去了大部分价值,因为没有人再关心该图表的重点。对于下一个问题,您需要另一个完全不同的图表和另一个焦点。

Some documentation is useful for only a limited period of time and can then be deleted. For example, you need a specific diagram while you’re designing around a problem. When you’re done with the problem, the diagram immediately loses most of its value because nobody cares anymore about the focus of that diagram. And for the next problem, you need another completely different diagram with another focus.

因此:请毫不犹豫地丢弃针对特定问题的文档。

Therefore: Don’t hesitate to throw away documentation that is specific to a particular problem.

当值得存档图表时,将其变成博客文章,以图表作为插图讲述故事。

When it’s worthwhile to archive a diagram, turn it into a blog post, telling the story with the diagram as an illustration.

一组重要的临时文档是有关规划的所有内容,例如用户故事以及有关估计、跟踪等的所有内容。用户故事仅在开发之前才有用。燃尽图仅在迭代期间有用。(您可能希望保留统计数据以便稍后检查计划和估计的难度,但这是不同的。)您可以在迭代后扔掉用户故事便签。

One important set of transient documentation is everything about planning, like the user stories and everything about estimation, tracking, and so on. A user story is useful only just before development. A burndown chart is useful only during an iteration. (You may want to keep the stats to check later how hard it is to plan and estimate, but that is something different.) You can throw away user story sticky notes after the iteration.

按需文档

On-Demand Documentation

最好的文档是您真正需要且适合实际目的的文档。实现这一目标的最佳方法是根据实际需要按需创建文档。

The best documentation is documentation that you really need and that suits actual purposes. The best way to achieve it is to create the documentation on demand, in response to actual needs.

你现在拥有的需求是真实的人所证明的需求。这并不是对某些人未来可能会发现有用的东西的猜测。你现在的需求是明确的、有目的的,可以用问题来表达。要创建的文档只需回答问题即可。这是一个简单的算法,用于决定何时创建有关哪个主题的文档。

A need you have right now is a proven need from a real person. It’s not a speculation of something that someone could find useful in some future. The need you have right now is precise and has a purpose, and it can be expressed as a question. The documentation to be created will just have to answer the question. This is a simple algorithm to decide when to create documentation about what topic.

因此:避免猜测应该记录什么。相反,请注意所有提出的问题或未提出但应该提出的问题,因为这表明某些知识需要记录下来。

Therefore: Avoid speculating on what should be documented. Instead, pay attention to all questions asked or questions that were not asked but that should have been asked, as signals that some knowledge needs to be documented.

即时文档

Just-in-Time Documentation

最好及时引入文档。对文档的需求是一种宝贵的反馈,是一种“知识差距”信号,应该触发一些文档行动作为响应。最重要的文档可能是缺少的文档。倾听知识上的挫折来决定何时填补空白。

Documentation is best introduced just in time. The need for documentation is a precious feedback, a “knowledge gap” signal that should trigger some documentation action in response. The most important bit of documentation may be the documentation that is missing. Listen to knowledge frustrations to decide when to fill the gap.

笔记

Note

及时记录文档的想法受到拉动系统精益的启发。拉 式系统是一种生产或服务流程,旨在根据客户的要求或在生产过程中的下一步需要时提供商品或服务。

The idea of just-in-time documentation is inspired by the pull system Lean. A pull system is a production or service process that is designed to deliver goods or services as they are required by the customer or, in the production process, when required by the next step.

您可能不会花时间对每个问题进行一些记录操作。需要一定的门槛:

You might not invest time in some documentation action on each question. There’s a need for some threshold:

  • 有些遵循“二法则”:当你必须回答同一个问题两次时,开始记录它。

  • Some follow the “Rule of Two”: When you must answer the same question twice, start documentation about it.

  • 开源项目有时依靠社区投票来决定花时间在什么上,包括文档。

  • Open-source projects sometimes rely on community votes to decide what to spend time on, including for the documentation.

  • 商业产品有时依赖网站分析来决定花时间在什么上,包括文档。

  • Commercial products sometimes rely on website analytics to decide what to spend time on, including for the documentation.

  • Peter Hilton 在《避免文档》中对此过程有自己的看法,这与二人规则类似:

    1. 不要写文档。

    2. 虚张声势,“它在维基百科上。”

    3. 还是不要写文档。

    4. 如果他们回来了,就假装不知道。

    5. 然后写吧。

    6. 后来,随意提及你“找到了它”。10

  • Peter Hilton on Documentation Avoidance has his own take on this process, which is similar to the Rule of Two:

    1. Don’t write the docs.

    2. Bluff, “it’s on the wiki.”

    3. Still don’t write the docs.

    4. If they come back, feign ignorance.

    5. Then write it.

    6. Later, casually mention that you “found it.”10

10.彼得·希尔顿, https://www.slideshare.net/pirhilton/documentation-avoidance

10.Peter Hilton, https://www.slideshare.net/pirhilton/documentation-avoidance

在实践中,您可以保持低技术含量:每次当您被要求提供您没有任何可用文档的信息时,将请求记录为墙上的便签。

In practice, you can keep it low tech: Every time you’re asked for information for which you don’t have any documentation already available, log the request as a sticky note on a wall.

每当您重复请求类似类型的信息时,您可以作为一个团队决定投入一些最少的工作来创建它,使用墙上的乡村投票机制。

Whenever you have repeated requests for a similar kind of information, you can decide as a team to invest some minimal work to create it, using a rustic voting mechanism on the wall.

开始手动和非正式。在团队仪式期间观察并讨论便签纸;如果团队决定的话,可以将它们扔掉或提升它们来清理自动化文档任务。

Start manual and informal. Observe and discuss the sticky notes during team ceremonies; throw them away or promote them to clean automated documentation tasks if that is what the team decides.

然后,首先使用任何现有的和临时支持进行交互式解释:浏览源代码、在 IDE 中搜索和可视化、在纸或白板上绘制草图,甚至使用 PowerPoint 或 Keynote 作为快速绘图板。(当您需要大量“复制-粘贴-稍微更改”类型的草图时,有时使用工具会更容易。)然后立即将解释的关键部分重构为文档的一小部分。通过与同事的互动,你知道解释的哪些部分是必不可少的。如果某件事难以理解或令人惊讶或“啊哈!” 时刻,那么它可能值得以后保留给其他人。

Then start by explaining interactively, using any existing and improvised support: browsing the source code, searching and visualizing in the IDE, sketching on paper or a whiteboard, or even using PowerPoint or Keynote as a quick drawing pad. (It’s sometimes easier to use a tool when you need a lot of “copy-paste-change a little” kinds of sketches.) Then immediately refactor the key parts of the explanation into a little section of documentation. You know what parts of the explanations are essential from the interactions with your colleagues. If something was difficult to understand or surprising or came as an “Aha!” moment, then it’s probably worth keeping for other people later.

Peter Hilton 还有另一个编写文档的绝妙技巧,他称之为“反向即时文档”:

Peter Hilton has another fantastic trick for write documentation, which he calls “reverse just-in-time doc”:

您可以通过在聊天室中提问(然后将他们的答案粘贴到文档中)来欺骗其他人编写 JIT 文档,而不是提前编写文档。11

Instead of writing documentation in advance, you can trick other people into writing JIT documentation by asking questions in a chat room (and then pasting their answers into the docs).11

11.彼得·希尔顿, https://www.slideshare.net/pirhilton/documentation-avoidance

11.Peter Hilton, https://www.slideshare.net/pirhilton/documentation-avoidance

尽早激发即时学习

Provoking Just-in-Time Learning Early

修复错误或进行从代码到生产的小改进是快速了解应用程序及其完整开发过程的好方法。这就是为什么许多公司将错误修复和小改进任务作为新员工立即入职流程的一部分。这就产生了对知识的需求,而知识的需求本身又引发了寻找知识来源的需求:人、文物等等。

Fixing bugs or making small evolutions, from the code to production, is a great way to quickly learn about an application and its complete development process. That’s why many companies include bug fixing and minor evolution tasks as part of the immediate onboarding process for newcomers. This creates needs for knowledge, which in itself triggers the need to find sources of knowledge: people, artifacts, whatever.

一些初创公司有一项政策,要求你必须在工作的头两天内,在一些指导下,自己将一些东西投入生产。这迫使您快速发现完整的流程和所有涉及的同事(如果有)。这也是信任的标志:您受到足够的信任,可以立即真正交付某些东西。这也是对流程、测试和部署充满信心的标志自动化策略。您不仅学习代码,还了解到您可以信任交付方法,并且更改的典型时间范围非常短。这也是获取流程新反馈的好方法。如果安装和必备工作站设置需要两天或更长时间,那么您不可能在两天内交付某些东西。如果在本地开发人员设置期间必须有人经常提供帮助,那么您至少需要更好的文档,或者最好是更好的流程自动化。这同样适用于完整的交付管道和任何其他事项。

Some startups have a policy that you must deliver something into production by yourself, with some guidance, within your first two days on the job. This forces you to quickly discover the full process and all colleagues involved, if any. It’s also a mark of trust: You are trusted enough to be allowed to deliver something immediately, for real. It’s also a mark of confidence in the process, the tests, and the deployment automation strategy. You not only learn the code, you also learn that you can trust the delivery approach and that the typical timeframe of a change is very short. It’s also a great way to get fresh feedback on the process. If the installation and the prerequisite workstation setup takes two days or more, there’s no way you can deliver something in two days. If someone has to help often during the local developer setup, then you need better documentation at a minimum or, preferably, better automation of the process. The same goes for the full delivery pipeline and any other matter.

如果你有一个奇怪的内部或专有的东西,新加入者必须学习,新加入者会告诉你,有一个标准的替代方案可供你切换。

If you have a weird in-house or proprietary stuff that new joiners have to learn, newcomers will tell you that there is a standard alternative that you could switch to.

惊讶报告

Astonishment Report

新人的超能力就是带来新鲜的视角。惊讶报告是一种简单而有效的工具,可用于了解应该记录哪些内容以及可以改进哪些内容。

Newcomers’ superpower is to be bring a fresh perspective. An astonishment report is a simple yet effective tool for learning both about what should be documented and what could be improved.

要求所有新人报告他们在工作第一天遇到的所有惊喜。即使他们来自同一家公司或具有相似的背景,他们也可能带来新鲜的观点。建议每个新人都保留一个笔记本,并在注意到令人惊讶的事情后立即做笔记,以免他们忘记。保持坦率至关重要,因此观察期应较短,例如两天或一周。即使是两天也可能足够适应,奇怪的事情不再那么奇怪了。根据备注进行改进。

Ask all newcomers to report all their surprises during their very first days on the job. Even if they come from the same company or from a similar background, they might bring fresh perspectives. Suggest that each newcomer keep a notebook and take notes immediately after noticing an astonishment before they forget it. It’s paramount to preserve the candor, so keep the observation period short, such as two days or a week. Even two days might be long enough to get so acclimated that weird stuff is no longer so weird. Improve based on the remarks.

包括一些前期文档

Including Some Upfront Documentation

成为你小时候希望身边有的成年人。写下您在开始该项目时希望拥有的文档。

Be the adult you wish you had around when you were a child. Write the documentation you wish you had when you started on this project.

—推特上的@willowbl00

—@willowbl00 on Twitter

有时,按需文档方法可以通过一些前期文档来补充。危险在于您可能会创建可能永远没有用的推测性文档。这样做的好处是,显然重要的知识可以帮助人们,而无需等待“二人法则”触发。

Sometimes an on-demand documentation approach can be supplemented by some upfront documentation. The danger is that you might create speculative documentation that may never be useful. The benefit is that obviously essential knowledge becomes available to help people without waiting for the Rule of Two to trigger.

想象一下自己是该项目的初学者,一无所知。如果您还记得加入时的情况,那就更简单了。然后创建您想要找到的理想文档。

Imagine yourself as a beginner on the project, knowing nothing. If you remember what it was like when you joined, it’s simpler. Then create the ideal documentation that you would have loved to find.

然而,知识的诅咒可能会使这种方法大多无效。你可能无法再想象不知道你现在知道的事情是什么感觉。

However, the curse of knowledge can make this approach mostly ineffective. You simply might not be able to imagine anymore what it’s like not knowing something that you know now.

提前猜测哪些信息对你还不认识的其他人有用,试图完成你无法预测的任务是极其困难的。尽管如此,一些启发式方法可以帮助您决定何时应该立即记录某条知识:

It’s extremely hard to guess in advance what information will be useful for other people you don’t know yet, trying to do tasks you can’t predict. Still, there some heuristics can help you decide when a piece of knowledge should be documented right now:

  • 每个人都同意应该记录下来。

  • Everybody agrees it should be documented.

  • 这是一个热门话题(有争议)。

  • It’s a hot topic (controversial).

  • 这个问题已经讨论了很长时间,比如在冲刺计划仪式上。

  • It’s been discussed for a long time, such as during the sprint planning ceremony.

  • 一些相关人员存在深刻或令人震惊的误解。

  • There has been a profound or shocking misunderstanding by some people involved.

  • 这很重要,而且无法从代码中猜测或推断出它。

  • It’s important, and there’s no way it can be guessed or inferred from the code.

  • 应该对其进行重构以避免需要文档,但现在这样做并不实际。Andy Schneider 12在每天改进文档方面有非常好的话语,重点是同理心:“让您所添加的价值永久化。”

  • It should be refactored to avoid the need for documentation, but it’s not practical to do that now. Andy Schneider12 has really nice words on improving the documentation every day, with a focus on empathy: “Make the value you’ve added permanent.”

12. Andy Schneider 是 OOPSLA 2001 软件考古学研讨会的参加者,他撰写了立场文件“软件考古学:理解大型系统”,https: //web.archive.org/web/20081121110405/ http://www.visibleworkings .com/archeology/schneider.html

12.Andy Schneider was an attendee of the OOPSLA 2001 Workshop on Software Archeology who wrote the position paper “Software Archaeology: Understanding Large Systems,” https://web.archive.org/web/20081121110405/http://www.visibleworkings.com/archeology/schneider.html

“注释你正在编写的代码,这样下一个人就不必经历同样的痛苦”这句格言并没有准确地告诉你何时或何时不做与文档相关的事情。还是要看你自己的判断了。但它让我们明白,一切都是为了保护他人的价值。

The maxim “Comment code that you are working on so the next person doesn’t have to go through the same pain” does not tell you precisely when or when not to do something documentation related. It’s still up to your judgment. But it brings home the point that it’s all about protecting value for other people.

激发按需文档的技术是借助技能矩阵或通过知识积压来定义文档的内容。

Techniques to stimulate on-demand documentation are to define the content of the documentation with the help of a skills matrix or through a knowledge backlog.

知识积压

对于知识积压,让每个团队成员在便利贴上写下他们想要的知识片段。然后让每个人把他们的笔记贴在墙上,并让他们通过协商一致或通过点投票来决定应该首先记录什么。这可能会成为你的知识积压。每隔几周或每次迭代,您可以采取一两个项目并决定如何解决它们,无论是通过结对编程、增强代码以使结构在代码本身中可见,还是将该领域的特定知识记录为常青文档在维基上。

For a knowledge backlog, let each team member write on sticky notes the pieces of knowledge they’d like to have. Then have everyone put their notes on a wall and have them decide by consensus or by voting with dots what should be documented first. This could become your knowledge backlog. Every few weeks or every iteration, you can take one or two items and decide how to address them, whether through pair programming, augmenting the code to make the structure visible in the code itself, or documenting specific knowledge of the area as an evergreen document on the wiki.

知识积压创建会议可以在您的回顾中完成。

The knowledge backlog creation session can be done within your retrospective.

但是,请注意积压的情况,并避免使用电子跟踪器;白板底部的贴纸就足够了,空间不足会提醒您保持积压量较小。

However, beware of backlogs growing and avoid using an electronic tracker; stickers at the bottom of your whiteboard are enough, and the lack of room will remind you to keep the backlog small.

技能矩阵

创建知识积压的另一种方法是创建一个包含预定义领域的技能矩阵,并要求每个团队成员声明他或她对每个领域的熟练程度。这里的一个限制是,矩阵将反映创建它的人的观点,并且会忽略该人忽略或忽视的技能领域。

An alternative to creating a knowledge backlog is to create a skills matrix with predefined areas and ask each team member to declare his or her level of proficiency for each area. One limitation here is that the matrix will reflect the views of the person creating it and will ignore the skills areas ignored or neglected by this person.

您可以将技能矩阵用作具有多个象限的图表,如 Jim Heidema 在博客文章中所述。13这是一个可以张贴在房间中的图表,用于确定所需的技能和团队成员。在左栏中,您列出了所有团队成员。在顶部,您列出了团队所需的所有各种技能。然后每个人回顾他或她的行,查看每项技能,然后根据图表下方的范围确定他或她可以填写每个圆圈的多少个象限。范围从没有技能到教授每列中的所有技能:

You could use a skills matrix as a chart with many quadrants, as described by Jim Heidema in a blog post.13 This is a chart that can be posted in the room to identify the skills needed and the people on the team. In the left column you list all the team members. Along the top you list all the various skills you need on the team. Then each person reviews his or her row, looking at each skill, and then identifies how many quadrants of each circle he or she can fill in, based on the range below the chart. The range is from no skills through teaching all skills in each column:

13. Jim Heidema,敏捷建议博客, http://www.agileadvice.com/2013/06/18/agilemanagement/leaving-your-title-at-the-scrum-team-room-door-and-pick-up -新技能/

13.Jim Heidema, Agile Advice blog, http://www.agileadvice.com/2013/06/18/agilemanagement/leaving-your-title-at-the-scrum-team-room-door-and-pick-up-new-skills/

0:无技能

0: no skill

1:基础知识

1: basic knowledge

2:执行基本任务

2: perform basic tasks

3:执行所有任务(专家)

3: perform all tasks (expert)

4:教授所有任务

4: teach all tasks

每当技能矩阵显示缺乏技能时,您就需要计划培训或以某种方式改进文档。

Whenever the skills matrix reveals a lack of skills, you need to plan a training or improve the documentation in some way.

交互式文档

Interactive Documentation

书面文件没有互动的机会。正如尤卡·科佩拉 (Jukka Korpela) 对 Wiio 法律的评论,每当书面文档“如一本书、一个网页或一篇报纸文章”奇迹般地发挥作用,那是因为作者参与了其他地方的对话。14

Written documents don’t have the opportunity for interaction. As Jukka Korpela comments on Wiio’s laws, whenever a written document “such as a book or a Web page or a newspaper article, miraculously works, it’s because the author participated in dialogues elsewhere.”14

14.尤卡·科佩拉, http://jkorpela.fi/wiio.html

14.Jukka Korpela, http://jkorpela.fi/wiio.html

要使书面文档发挥作用,仅仅输入文本需要做更多的工作。乔治·丁维迪(George Dinwiddie)在他的博客中建议“记录读者可能有的问题”并“让多人审阅”。15书面文档应该像一次有效的交互式对话的记录,这样更有可能再次发挥作用。

It takes more work than just typing text for a written document to be useful. George Dinwiddie advises in his blog to “document questions the reader may have” and to “get it reviewed by multiple people.”15 Written documentation should be like a record of an interactive conversation that worked, which makes it more likely to work again.

15.乔治·丁威迪, http://blog.gdinwiddie.com/2010/08/06/the-use-of-documentation/

15.George Dinwiddie, http://blog.gdinwiddie.com/2010/08/06/the-use-of-documentation/

但借助我们周围可用的技术,您也可以突破纸上书面文字的限制。您可以创建在某种程度上具有交互性的文档。

But you can also push the limits of written words on paper, thanks to the available technologies all around us. You can create documentation that is interactive to some extent.

例如,Gojko Adzic 将测试启发式检查表变成了浏览器中的附加菜单,作为一个名为 BugMagnet 的小助手(见图10.6)。16

For example, Gojko Adzic turned a checklist of test heuristics into an additional menu in the browser, as a small assistant called BugMagnet (see Figure 10.6).16

图中显示了bug Magnet的子菜单,其中“名称”的子菜单进一步显示了某些名称。

图 10.6 BugMagnet

Figure 10.6 BugMagnet

16.BugMagnet https://github.com/gojko/bugmagnet

16.BugMagnet, https://github.com/gojko/bugmagnet

单击“名称”,然后单击菜单中的“NULL”,将直接使用字符串填充浏览器中的编辑字段"NULL"。这本来可以是一个简单的清单,可以手动输入到表单中,但 Gojko 采取了额外的步骤,使其更具交互性。请注意导航菜单的暗示效果:它要求使用,至少比打印的清单更多。

Clicking on Names and then NULL in the menu directly fills the edit field in the browser with the string "NULL". This could have remained a plain checklist to input manually into forms, but Gojko took the extra step of making it a little more interactive. Note the suggestive effect of navigating the menu: It calls for being used, at least more than a printed checklist.

因此:只要有可能,就应该选择交互式文档,而不是静态的书面文字。使用超媒体通过链接导航到内容。将文档转变为检查器、任务助手或搜索引擎等工具。

Therefore: Whenever possible, prefer documentation that is interactive over static written words. Use hypermedia to navigate to the content through links. Turn the documentation into tools like checkers, tasks assistants, or search engines.

您已经了解了交互式文档的几个示例:

You already know several examples of interactive documentation as it is all around:

  • 具有可导航链接的超媒体文档,由 Javadoc 和其他语言的等效系统生成。

  • Hypermedia documentation with navigable links, as generated by Javadoc and equivalent systems in other languages.

  • 像 Pickles 这样的工具可以将 Cucumber 或 SpecFlow 报告变成交互式网站,或者 Fitnesse,它从一开始就是交互式的。

  • Tools like Pickles that turn Cucumber or SpecFlow reports into interactive websites, or Fitnesse, which has been interactive from the start.

  • Swagger 等工具可将 Web API 记录到交互式网站中,并具有直接发送请求和显示响应的内置功能。

  • Tools like Swagger that document a web API into an interactive website, with built-in capability to directly send requests and show the responses.

  • 您的 IDE,通过击键或单击鼠标即可提供大量文档功能:调用堆栈、搜索类型或引用、类型层次结构、查找出现的情况、在编程语言抽象语法树中查找等。

  • Your IDE, which offers a lot of documentation features with a keystroke or a mouse click: call stack, search for type or reference, type hierarchy, find occurrences, find in the programming language abstract syntax tree, and so on.

如下一节所述,将文档放入可读的自动化形式可以实现交互式发现:您可以执行和修改自动化代码(脚本和测试),以便在更改主题时更深入地理解主题并查看主题影响。

As described in the next section, putting documentation into an automated form that is also readable allows for interactive discovery: You can execute and tinker with the automation code (scripts and tests) to understand the topic in more depth as you change it and see the effects.

声明式自动化

Declarative Automation

每次自动化软件任务时,您都应该抓住机会将其变成文档形式。软件开发的各个方面越来越多地利用自动化。在过去的几十年里,流行的工具改变了我们的工作方式,用自动化流程取代了重复的手动任务。持续集成工具可以自动从源头构建软件,并且可以自动执行测试,甚至在远程目标计算机上也是如此。

Every time you automate a software task, you should take the opportunity to make it a form of documentation as well. Software development is increasingly making use of automation in all its aspects. Over the past decades, popular tools have changed the way we work, replacing repetitive manual tasks with automated processes. Continuous integration tools automate the building of software from its source, and they automate tests executions, even on remote target machines.

Maven、NuGet 和 Gradle 等工具可自动完成检索所有所需依赖项的负担。Ansible、Chef 和 Puppet 等工具声明并自动化所有 IT 基础设施的配置。

Tools like Maven, NuGet, and Gradle automate the burden of retrieving all the required dependencies. Tools like Ansible, Chef, and Puppet declare and automate the configuration of all IT infrastructures.

这种趋势有一些有趣的地方:你必须描述你想要什么才能实现自动化。您声明该流程,然后该工具会解释它来执行此操作,这样您就不必这样做。好消息是,当您声明流程时,您就是在记录它 - 不仅为机器,也为人类,因为您也必须维护它。

There’s something interesting in this trend: You must describe what you want in order to automate it. You declare the process, and then the tool interprets it to do it so that you don’t have to. The good news is that when you declare the process, you are documenting it—not just for the machine but also for humans as you have to maintain it, too.

因此:每当您自动化流程时,请抓住机会使其成为该流程的主要文档形式。与依赖规范性脚本风格的工具相比,更喜欢具有声明性配置风格的工具。确保声明性配置主要针对人类受众,而不仅仅是针对工具。

Therefore: Whenever you automate a process, take the opportunity to make it the primary form of documentation for this process. Favor tools with a declarative style of configuration over tools that rely on a prescriptive style of scripts. Make sure the declarative configuration is meant primarily for a human audience, not only for the tool.

目标是使声明性配置成为流程的单一事实来源。这是文档的一个很好的例子,它既是人类的文档,也是机器的文档。

The goal is for the declarative configuration to be the single source of truth for the process. This is a great example of documentation that is both documentation for humans and documentation for machines.

在所有新的自动化工具出现之前我们做了什么?在最坏的情况下,该过程是由具有隐性知识的人手动完成的。当他或她不在的时候,根本没有办法做到这一点。当我们幸运一点时,有一个 Microsoft Word 文档以文本和命令行的混合方式描述了该过程。然而,当你尝试使用它的几次时,如果不向作者提出问题,你就很难成功:有些部分缺失,有些部分已经过时,并且指示错误。这是一个带有欺骗性文档的手动过程。当我们非常幸运时,有一个脚本可以自动化该过程。然而,当它抛出错误时,我们不得不再次向作者寻求帮助来修复它,因为脚本代码非常晦涩。还有一个单独的 Microsoft Word 文档,不完整且过时,假装描述该过程以取悦管理层。这是一个自动化过程,但仍然没有有用的文档。

What did we do before all the new automation tools? In the worst case, the process was done manually by someone with tacit knowledge of how to do it. When he or she was away, there was no way to do it at all. When we were a little luckier, there was a Microsoft Word document describing the process in a mix of text and command lines. However, the few times you tried to use it, you could hardly succeed without asking questions of the author: Some parts were missing and other were obsolete, with wrong indications. It was a manual process with deceiving documentation. When we were really lucky, there was a script to automate the process. However, when it was throwing errors, we again had to ask the author for help to fix it, as the script code was quite obscure. And there was a separate Microsoft Word document, incomplete and obsolete, pretending to describe the process to please the management. It was an automated process, but still with no useful documentation.

现在我们知道得更清楚了,解决所有早期问题的关键词是声明性自动化

Now we know better, and the key words to fix all the earlier problems are declarative and automation.

陈述式风格

Declarative Style

对于要被视为文档的工件,它必须具有表现力并且易于人们理解。它还应该解释意图和高层决策,而不仅仅是如何实现它的细节。

For an artifact to be considered documentation, it must be expressive and easy to understand by people. It should also explain the intentions and the high-level decisions, not just the details of how to make it happen.

一步步规定要做什么的命令式脚本对于任何重要的自动化来说都是失败的。他们只关注“如何”,而导致“如何”的所有有趣的决定和反思只能通过评论来表达(如果有的话)。

Imperative scripts that prescribe, step by step, what to do fail for any nontrivial automation. They only focus on the how, whereas all the interesting decisions and reflections that led to the how can only be expressed through comments, if at all.

另一方面,声明性工具在支持良好的文档方面更加成功,这要归功于两个因素:

On the other hand, declarative tools are more successful at supporting nice documentation, thanks to two factors:

  • 他们已经知道如何做很多典型的低级事情,这些事情已经被专门的开发人员很好地编码成可重用的现成模块。这是一个抽象层。

  • They already know how to do a lot of typical low-level things, which have been codified well by dedicated developers into reusable ready-made modules. This is an abstraction layer.

  • 它们在顶部提供了一种声明性的特定于领域的语言,同时更加简洁和富有表现力。该 DSL 是标准的,并且本身有详细的文档记录,这使得它比您的内部脚本语言更易于访问。该 DSL 通常以无状态和幂等的方式描述所需的状态;通过将当前状态移出图片之外,解释变得更加简单。

  • They offer a declarative domain-specific language on top, which is at the same time more concise and expressive. This DSL is standard and is itself well documented, which makes it more accessible than your in-house scripting language. This DSL usually describes the desired state in a stateless and idempotent fashion; by moving the current state out of the picture, the explanations become much simpler.

自动化

自动化对于迫使所公开的知识诚实至关重要。使用现代自动化方法,您往往会非常频繁地运行该过程,甚至连续运行或每小时运行数十次。保持其可靠性和始终最新的压力很大。您必须聪明才能减少维护。因此,您所依赖的自动化充当了一种协调机制,当所声明的流程出现错误时,该机制会变得显而易见。

Automation is essential to force declared knowledge to be honest. With modern approaches to automation, you tend to run the process very often, even continuously or dozens of times per hour. There is good pressure to keep it reliable and always up-to-date. You have to be smart to reduce the maintenance. Automation you rely on therefore acts as a reconciliation mechanism that makes it obvious when the declared process becomes wrong.

已经发生了一场革命,或者也许是一场演变。最后,你可以获得最新的知识,并且真正解释你想要什么,以及你谈论它的方式。工具越来越接近于服务于我们的思维方式,这在许多方面改变了游戏规则,特别是在文档方面。

There has been a revolution, or perhaps an evolution. At last you can have knowledge that is up-to-date and that really explains what you want, the way you would talk about it. Tools are getting closer to serving the way we think, and that’s changing the game in many aspects, in particular with respect to documentation.

声明式依赖管理

Declarative Dependency Management

在构建自动化领域,依赖管理器(也称为包管理器)是在构建过程中发挥关键作用的工具。它们可靠地下载库,包括其传递依赖项,解决许多冲突,并支持您的依赖项管理策略,甚至跨多个模块也是如此。

In the build automation landscape, dependency managers, also known as package managers, are tools that play a key role in the build process. They reliably download libraries, including their transitive dependencies, resolve many of the conflicts, and support your dependency management strategy, even across many modules.

在实现自动化之前,依赖关系管理是一项手动完成的苦差事。您可以手动将某些版本的库下载到 /lib 文件夹中,然后存储在源代码控制系统中。如果依赖项有依赖项,您还必须查看网站并下载所有这些依赖项。每当您必须切换到依赖项的新版本时,您都必须重做所有这些。这并不有趣。

Before that automation, dependency management was a chore done manually. You would manually download the libraries in some version into a /lib folder, later stored in the source control system. If the dependency had dependencies, you had to look at a website and download all of those, too. And you had to redo all of this whenever you had to switch to a new version of a dependency. It was not fun.

流行的依赖管理器适用于大多数编程语言:Apache Maven 和 Apache Ivy (Java)、Gradle(Groovy 和 JVM)、NuGet (.Net)、RubyGems (Ruby)、sbt (Scala)、npm (Node.js)、Leiningen (Clojure)、Bower (网络) 等等。

Popular dependency managers are available for most programming languages: Apache Maven and Apache Ivy (Java), Gradle (Groovy and JVM), NuGet (.Net), RubyGems (Ruby), sbt (Scala), npm (Node.js), Leiningen (Clojure), Bower (web), and many others.

为了完成自动化工作,这些工具需要您声明您期望的所有直接依赖项。您通常在一个简单的文本文件(通常称为清单)中执行此操作。该清单是物料清单,它规定了构建应用程序时要检索的内容。

To do their job of automating, these tools need you to declare all the direct dependencies you expect. You usually do this in a simple text file, often called a manifest. This manifest is the bill of materials that dictates what to retrieve in order to build your application.

使用 Maven 时,声明是在名为 pom.xml 的 XML 清单中完成的:

When using Maven, the declaration is done in an XML manifest called pom.xml:

1 <依赖项>
2 <groupId>com.google.guava</groupId>
3 <artifactId>番石榴</artifactId>
4 <版本>18.0</版本>
5 </依赖>
1  <dependency>
2  <groupId>com.google.guava</groupId>
3  <artifactId>guava</artifactId>
4  <version>18.0</version>
5  </dependency>

在 Leiningen,声明是在 Clojure 中完成的:

In Leiningen, the declaration is done in Clojure:

1 [com.google.guava/guava“19.0-rc1”]
1  [com.google.guava/guava "19.0-rc1"]

无论语法如何,预期依赖项的声明始终出现在三个值的元组中:组 ID、工件 ID 和请求的版本。

Whatever the syntax, the declaration of the expected dependencies always happens in a tuple of the three values: group ID, artifact ID, and requested version.

在某些工具中,请求的版本不仅可以是版本号,例如18.0,还可以是范围,例如[15.0,18.0)(表示从版本 15.0 到版本 18.0 除外),或者特殊关键字,例如LATESTRELEASESNAPHOTALPHABETA。您可以从这些范围和关键字的概念中看到,这些工具已经学会在我们作为开发人员所认为的同一抽象级别上工作。表达必要依赖关系的语法是声明性的,这是一件好事。

In some of the tools, the requested version can be not only a version number, like 18.0, but a range such as [15.0,18.0) (meaning from version 15.0 to version  18.0 exclusive), or a special keyword such as LATEST, RELEASE, SNAPHOT, ALPHA, or BETA. You can see from these concepts of range and keywords that the tools have learned to work at the same level of abstraction we think at as developers. The syntax to express the necessary dependencies is declarative, and this is a good thing.

通过声明式自动化,所请求的依赖项的声明也是依赖项文档的单一事实来源。知识已经存在于依赖清单中。因此,无需在另一个文档或 wiki 中再次列出这些依赖项。如果您制定了这样的列表,您就有可能忘记更新它。

With declarative automation, the declaration of the requested dependencies is also the single source of truth for the documentation of the dependencies. The knowledge is already there, in the dependency manifest. As a consequence, there is no need to list these dependencies again in another document or in a wiki. If you made such a list, you would just risk forgetting to update it.

但是,与往常一样,到目前为止,在依赖项声明中还缺少一件事:您不仅需要声明从工具请求的内容,还需要声明相应的基本原理。您需要记录其基本原理,以便将来的新手能够快速掌握所包含的每个依赖项背后的原因。添加一个依赖项永远不会太容易,因此总是能够用令人信服的理由来证明依赖项的合理性是件好事。一种方法是在文件中的依赖项旁边添加注释:

But, as usual, there’s one thing missing so far in the declaration of the dependencies: You’d like to declare not just what you request from the tool but also the corresponding rationale. You need to record the rationale so that future newcomers can quickly grasp the reason behind each dependency included. Adding one more dependency should never be done too easily, so it’s good to always be able to justify a dependency with a convincing reason. One way to do this is with comments next to the dependency entries in the file:

1 <依赖项>
2 <!-- 基本原理:一个非常轻量级的 JDBC 替代方案,
没有魔法-->
3 <依赖关系>
4 <groupId>org.jdbi</groupId>
5 <artifactId>jdbi</artifactId>
6 <版本>2.63</版本>
7 </依赖>
8 <依赖项/>
1  <dependencies>
2    <!-- Rationale: A very lightweight alternative to JDBC,
with no magic -->
3    <dependency>
4       <groupId>org.jdbi</groupId>
5       <artifactId>jdbi</artifactId>
6       <version>2.63</version>
7    </dependency>
8  <dependencies/>

您可能想添加描述,但您不必这样做,因为它已经包含在依赖项本身的 POM 中。在 Eclipse 等 IDE 中,通过按 Ctrl(或 Mac 上的 Cmd)可以轻松导航到依赖项的 POM。当鼠标悬停在 POM 中的依赖项元素上时,它会变成一个链接,允许您直接跳转到依赖项的 POM,如图10.7所示。这是与声明性自动化混合的集成文档。纯粹的敬畏!

You might be tempted to add a description, but you don’t have to because it’s already included in the POM of the dependency itself. In an IDE such as Eclipse, it’s very easy to navigate to the POM of the dependency by pressing Ctrl (or Cmd on a Mac). As your mouse hovers over the dependency element in your POM, it turns into a link that allows you to directly jump to the POM of the dependency, as shown in Figure 10.7. That’s integrated documentation mixed with declarative automation. Pure awesomeness!

该图显示了 Eclipse POM 编辑器中 Maven 依赖项的导航。

图 10.7 在 Eclipse POM 编辑器中导航 Maven 依赖项

Figure 10.7 Navigating the Maven dependencies in the Eclipse POM Editor

有关依赖项及其版本的知识是否可以访问?这取决于观众。对于开发人员来说,最方便的方法是查看清单并使用 IDE,因此无需执行任何其他操作。一个问题可能是当使用版本的范围或关键字,仅通过查看清单您不知道在给定时间点检索到的确切版本。但是,开发人员知道如何查询依赖关系管理器以按需获取此信息。例如,在 Maven 中,他们将运行以下命令:

Is that knowledge on the dependencies and their version accessible? It depends on the audience. For developers, the most accessible way is to look at the manifest and use the IDE, so there’s no need to do anything more. One issue may be that when using ranges or keywords for the versions, you don’t know the exact version being retrieved at a given point in time just by looking at the manifest. However, developers know how to query the dependency manager to get this information on demand. For example, in Maven they would run the following:

mvn 依赖项:tree -Dverbose
mvn dependency:tree -Dverbose

对于非开发人员,您可能希望提取有趣的内容并将其发布到 Excel 文档或 wiki 中。但非开发人员真的对这类知识非常感兴趣吗?

For nondevelopers, you would want to extract and publish the interesting content into an Excel document or to the wiki. But are nondevelopers really very interested in that kind of knowledge?

声明式配置管理

Declarative Configuration Management

抱歉,这花了这么长时间,我丢失了我的 bash 历史记录,因此不知道我们上次是如何解决这个问题的。

Sorry this is taking so long, I lost my bash history and therefore have no idea how we fixed this last time.

—@honest Twitter 上的状态页面

—@honest status page on Twitter

配置管理比依赖管理复杂得多。它涉及应用程序、守护程序和文件等资源,每​​个资源都具有许多属性及其所有依赖项。然而,一些工具采用了类似于依赖管理器及其清单的声明性方法。使用这些工具时,您不应该过多使用命令行,与图 10.7中的情况相反。

Configuration management is much more complex than dependency management. It involves resources such as applications, daemons, and files, each with many attributes and with all their dependencies. However, some tools have taken a declarative approach similar to that of the dependency managers and their manifests. When using these tools, you are not supposed to use the command line much, as opposed to the situation in Figure 10.7.

最流行的配置管理工具是 Ansible、Puppet、CfEngine、Chef 和 Salt。然而,其中一些是命令式的(Chef),而另一些则是声明式的(Puppet 和 Ansible)。

The most popular tools for managing configuration are Ansible, Puppet, CfEngine, Chef, and Salt. However, some of them are imperative (Chef), while others are declarative (Puppet and Ansible).

例如,Ansible 声称它“使用一种非常简单的语言 [...],允许您以接近简单英语的方式描述您的自动化工作” 17,这是典型的声明式方法,正如 Big Panda 博客中所解释的那样:

For example, Ansible states that it “uses a very simple language […] that allow you to describe your automation jobs in a way that approaches plain English,”17 which is typical of a declarative approach, as explained on the Big Panda blog:

17. https://www.ansible.com/overview/how-ansible-works

17.https://www.ansible.com/overview/how-ansible-works

Ansible 的理念是 playbook(无论是服务器配置、服务器编排还是应用程序部署)应该是声明性的。这意味着编写剧本不需要了解服务器的当前状态,只需要了解其所需的状态。18

Ansible’s philosophy is that playbooks (whether for server provisioning, server orchestration or application deployment) should be declarative. This means that writing a playbook does not require any knowledge of the current state of the server, only its desirable state.18

18. https://www.bigpanda.io/blog/5-reasons-we-love-using-ansible-for-continuous-delivery/

18.https://www.bigpanda.io/blog/5-reasons-we-love-using-ansible-for-continuous-delivery/

Puppet 也有类似的理念。以下是用于管理 NTP 的 Puppet 清单的摘录:

Puppet has a similar philosophy. Here’s an excerpt of a Puppet manifest for managing NTP:

1 # 如果有必要的话一些评论...
2 服务 { 'ntp':
3 名称 => $service_name,
4 确保=>运行,
5 启用 => true,
6 订阅 => 文件['ntp.conf'],
7}
8
9 文件 { 'ntp.conf':
10 路径 => '/etc/ntp.conf',
11 确保=>文件,
12 需要 => 包['ntp'],
13 源=>“puppet:///modules/ntp/ntp.conf”,
14}
1  # Some comment if necessary...
2  service { 'ntp':
3    name  => $service_name,
4    ensure  => running,
5    enable  => true,
6    subscribe => File['ntp.conf'],
7  }
8
9  file { 'ntp.conf':
10   path  => '/etc/ntp.conf',
11   ensure  => file,
12   require => Package['ntp'],
13   source  =>  "puppet:///modules/ntp/ntp.conf",
14 }

Puppet 强调,其清单是自我记录的,甚至可以为许多监管机构提供合规证明:

Puppet emphasizes that its manifests are self-documented and provide proof of compliance even for many regulatory bodies:

自我记录

Self-documentation

Puppet 清单非常简单,任何人都可以阅读和理解它们,包括 IT 和工程部门之外的人员。

Puppet manifests are so simple, anyone can read and understand them, including people outside your IT and engineering departments.

可审计性

Auditability

无论是外部审核还是内部审核,如果能够证明您已通过,那就太棒了。您可以轻松地向自己的主管验证是否已满足合规性要求。19

Whether it’s an external or internal audit, it’s great to have proof that you pass. And you can easily validate to your own executives that compliance requirements have been met.19

19. Puppet 博客, https://puppetlabs.com/blog/puppets-declarative-language-modeling-instead-of-scripting

19.Puppet blog, https://puppetlabs.com/blog/puppets-declarative-language-modeling-instead-of-scripting

像这些工具中使用的声明性语言不仅可以让您向团队成员传达预期的状态,还可以向团队中的其他人员或外部审计员传达预期的状态。

A declarative language like the ones used in these tools allows you to communicate the expected desired state not only to the too, but to the other humans on your team or to external auditors.

同样,为了使这些清单成为对人类来说完整且有用的文档,通常缺少的是每个决定的基本原理。如果您认为所有感兴趣的受众都可以按原样访问 Puppet 清单,那么将基本原理和其他高级信息记录到清单中(例如,带有注释)是有意义的。

Again, what’s often missing to make these manifests complete and useful documentation for humans is the rationale for each decision. If you consider that a Puppet manifest as-is is accessible to all the interested audience, then it would make sense to document the rationales and other high-level information into the manifest—for example, with comments.

因为有关配置的知识是以工具的正式方式声明的,所以当它可以帮助推理时,也可以生成生动的图表。例如,Puppet 包含一个图形选项,可生成显示所有依赖关系的图表的 .dot 文件。当您遇到依赖项中的问题或想要更直观地了解清单中的内容时,这非常有用。

Because the knowledge about the configuration is declared in a formal way for the tools, it also becomes possible to generate living diagrams when it can help reasoning. For example, Puppet includes a graph option that generates a .dot file of a diagram showing all the dependencies. This is useful when you experience an issue in the dependencies or when you want to have a more visual view of what’s in the manifests.

图 10.8显示了从 Puppet 生成的图表示例。20

Figure 10.8 shows an example of a diagram generated from Puppet.20

该图显示了 Puppet 生成的资源依赖关系的流程图。

图 10.8 Puppet 生成的资源依赖关系图

Figure 10.8 A diagram of resources dependencies, generated by Puppet

20.约翰·阿伦德尔, http://bitfieldconsulting.com/puppet-dependency-graphs

20.John Arundel, http://bitfieldconsulting.com/puppet-dependency-graphs

这种图表可以方便地重构清单,使它们更干净、更简单、更模块化。正如 John Arundel 在他的博客中描述 Puppet 的这一功能所写的那样:

This kind of diagram can be handy for refactoring the manifests to make them cleaner, simpler, and more modular. As John Arundel writes in his blog describing this feature of Puppet:

当您开发 Puppet 清单时,您有时需要重构它们,使它们更干净、更简单、更小且更模块化,并且查看图表对于此过程非常有帮助。一方面,它可以帮助明确需要进行一些重构。21

As you develop Puppet manifests, from time to time you need to refactor them to make them cleaner, simpler, smaller and more modular, and looking at a diagram can be very helpful with this process. For one thing, it can help make it clear that some refactoring is needed.21

21.约翰·阿伦德尔, http://bitfieldconsulting.com/puppet-dependency-graphs

21.John Arundel, http://bitfieldconsulting.com/puppet-dependency-graphs

声明式自动化部署

Declarative Automated Deployment

与配置管理非常相似,许多工具可以自动化您的部署,包括必要的公司工作流程和回滚过程,并且可以仅部署需要更改的内容。其中一些工具包括带有自定义或标准插件的 Jenkins 和 Octopus Deploy (.Net)。

Much as with configuration management, a number of tools can automate your deployment, including the necessary company workflows and rollback procedures, and that can deploy only what needs to be changed. Some of these tools include Jenkins with custom or standard plugins and Octopus Deploy (.Net).

以下是 Octopus 网站上的部署工作流程示例:22

Here’s an example of a deployment workflow from the Octopus website:22

22. https://octopus.com/blog/octopus-vs-puppet-chef

22.https://octopus.com/blog/octopus-vs-puppet-chef

  • 将负载均衡器重定向到“停机维护”站点

  • Redirect load balancer to a “down for maintenance” site

  • 从负载均衡器中删除 Web 服务器

  • Remove web servers from load balancer

  • 停止应用程序服务器

  • Stop application servers

  • 备份和升级数据库

  • Back up and upgrade the database

  • 启动应用程序服务器

  • Start application servers

  • 将 Web 服务器添加回负载均衡器

  • Add web servers back to load balancer

在这样的工具中,部署和发布工作流程通常通过单击 UI 来设置,并保存在数据库中。尽管如此,工作流程还是以声明性方式描述的,每个人在查看工具屏幕时都可以理解。每当您想知道它是如何完成时,您只需在工具中查找即可。

In a tool like this, the deployment and release workflow is typically set up by clicking on the UI, and it is persisted in a database. Still, the workflow is described in a declarative manner that everyone can understand when looking at the tool screens. Whenever you want to know how it’s done, you just have to look it up in the tool.

因为它是声明性的,并且因为该工具了解部署的基础知识,所以可以以简洁的方式描述复杂的工作流程,更接近我们的思考方式。例如,可以应用标准的持续交付模式,例如金丝雀发布和蓝绿部署。Octopus Deploy 通过一个名为Lifecycle 的概念来管理这一点,这个抽象对于轻松处理此类策略非常有用。

Because it’s declarative and because the tool knows about the basics of deployment, it is possible to describe complex workflows in a concise way, closer to the way we think about it. For example, it is possible to apply standard continuous delivery patterns such as canary releases and blue-green deployment. Octopus Deploy manages that with a concept call Lifecycle, an abstraction that is useful for easily taking care of this kind of strategy.

工具不仅可以自动化工作本身并减少出错的可能性,还可以为您可以或应该使用的标准模式提供现成的文档。因此,这是您不必自己编写的更多文档!

Tools can not only automate the work itself and reduce the likelihood of errors but also provide ready-made documentation for the standard patterns you could, or should, be using. This is therefore more documentation you don’t have to write by yourself!

假设您决定为您的应用程序采用蓝绿部署。您可以配置该工具来处理它,这就是您现在所要做的:

Say that you decide to adopt a blue-green deployment for your application. You can configure the tool to take care of it, and this is all you have to do now:

  • 在稳定文档(例如自述文件)中声明您已决定进行蓝绿部署。

  • Declare in a stable document such as a README file that you have decided to do blue-green deployments.

  • 链接到该主题的权威文献,例如 Martin Fowler 网站上的模式。23

    23. Martin Fowler,ThoughtWorks, http://martinfowler.com/bliki/BlueGreenDeployment.html

  • Link to authoritative literature on the topic, such as the pattern on Martin Fowler’s website.23

    23.Martin Fowler, ThoughtWorks, http://martinfowler.com/bliki/BlueGreenDeployment.html

  • 配置工具和生命周期以支持该模式。

  • Configure the tool and the lifecycle to support the pattern.

  • 链接到工具网站上的页面,该页面描述了如何在工具中专门处理该模式。

  • Link to the page on the tool website that describes how the pattern is taken care of specifically in the tool.

以下是该工具上下文中该模式的描述:

The following is a description of the pattern in the context of the tool:

暂存:当蓝色处于活动状态时,绿色将成为下一次部署的暂存环境。

Staging: when blue is active, green becomes the staging environment for the next deployment.

回滚:我们部署到蓝色并使其处于活动状态。然后发现一个问题。由于 green 仍然运行旧代码,因此我们可以轻松回滚。

Rollback: we deploy to blue and make it active. Then a problem is discovered. Since green still runs the old code, we can roll back easily.

灾难恢复:部署到蓝色并且我们对其稳定感到满意后,我们也可以将新版本部署到绿色。这为我们提供了灾难发生时的备用环境。24

Disaster recovery: after deploying to blue and we’re satisfied that it is stable, we can deploy the new release to green too. This gives us a standby environment ready in case of disaster.24

24.章鱼部署, http://docs.octopusdeploy.com/display/OD/Blue-green+deployments

24.Octopus Deploy, http://docs.octopusdeploy.com/display/OD/Blue-green+deployments

对于提供文档的声明式自动化的自动化来说,工具的配置必须是真正的声明式,无论是在文本中还是在屏幕上和数据库中。它还必须处于抽象级别,接近对每个参与者来说重要的事情。它不能是带有大量基于低级细节(例如文件不存在或操作系统进程的状态)的条件的模糊命令步骤。

For an automation to be a case of declarative automation that provides documentation, the configuration of the tool has to be genuinely declarative, whether in text or on a screen and in a database. It also has to be at an abstraction level close to what matters for everyone involved. It cannot be obscure imperative steps with a lot of conditionals based on low-level details such as the absence of a file or the state of an operating system process.

搭建分步指南

每当您加入一个新团队或一个新项目时,您都需要设置您的工作环境,并且需要一些相关文档 - 至少在许多公司中仍然如此。维基上可能有一个新人页面,其中包含一长串开始处理应用程序所需执行的步骤。这样的列表通常不是完全最新的。链接可能已损坏。重要的信息可能会丢失,因为这些信息在作者的脑海中是显而易见的。即使新人定期加入,此类问题仍然存在。

Whenever you join a new team or a new project, you need to set up your work environment, and you need some documentation for that—at least this is how it still goes in many companies. There may be a Newcomers page on the wiki with a long list of steps to go through in order to start working on an application. Such a list is often not totally up-to-date. Links may be broken. Essential information may be missing because it was obvious in the mind of the author. Such issues exist even when newcomers join regularly.

一些团队采取了更进一步的措施,为新人提供安装程序。您运行安装程序,它会提示您输入一些特定问题,然后您就完成了!当这些安装程序是自定义的内部脚本时,它们并不总是工作得很好,但想法是存在的:为什么要以文本形式记录可以自动化和记录的内容作为工具?

Some teams have taken a further step, providing an installer for newcomers. You run the installer, it prompts for some specific questions, and you’re done! These installers don’t always work very well when they’re custom in-house scripts, but the idea is there: Why document in text what could be automated, and documented, as a tool?

这种方法通常称为脚手架,不仅适合新手,还可以让用户快速启动应用程序。Ruby on Rails 可能是这种方法中最流行的工具。

This approach, often called scaffolding, is not just for newcomers, but also allows users to start an application quickly. Ruby on Rails is probably the most popular tool for this approach.

许多工具可以用来做脚手架。您可以使用自定义脚本、Maven 原型、Spring Roo、JHipster 等进行搭建。配置管理工具有时还可用于为新团队成员创建工作设置或设置稍后可以修改的应用程序模板。

Many tools can be used to do scaffolding. You can do scaffolding with custom scripts, Maven archetypes, Spring Roo, JHipster, and many others. Configuration management tools can sometimes also be used to create a working setup for new team members or to set up templates of applications that can be modified later.

如果最终的自动化是坚如磐石,那么它所做的事情的文档就不是什么问题,但总的来说,我更喜欢标准工具而不是内部脚本,而且我肯定会选择本身有良好文档记录和维护并且具有声明性的工具配置本身可以被视为文档。

If the resulting automation is rock solid, documentation of what it does is less of an issue, but in general I favor standard tools over in-house scripts, and I definitely would choose tools that are themselves well documented and maintained and that have a declarative configuration that can be considered itself as the documentation.

脚手架必须非常易于使用,无需用户指南。它应该提出简单的问题,逐步指导用户,提供合理的默认值,并提供非常好的答案示例。

The scaffolding has to be really easy to use without a user guide. It should ask simple questions, guide the user step by step, provide sensible default values, and have very good examples of answers.

有一个名为 JHipster 的开源脚手架工具。25它与命令行向导配合使用,以下是从头开始创建新应用程序时提示的一些问题:

There is an open-source tool for scaffolding called JHipster.25 It works with a command-line wizard, and here are some of the questions prompted when creating a new application from scratch:

25.JHipster http://drissamri.be/blog/technology/starting-modern-java-project-with-jhipster/

25.JHipster, http://drissamri.be/blog/technology/starting-modern-java-project-with-jhipster/

  • 您的应用程序的基本名称是什么?

  • What is the base name of your application?

  • 您想使用 Java 8 吗?

  • Do you want to use Java 8?

  • 您想使用哪种类型的身份验证?

  • Which type of authentication would you like to use?

  • 您想使用哪种类型的数据库?

  • Which type of database would you like to use?

  • 您想使用哪个生产数据库?

  • Which production database would you like to use?

  • 你想使用Hibernate二级缓存吗?

  • Do you want to use Hibernate second-level cache?

  • 您想使用集群 HTTP 会话吗?

  • Do you want to use clustered HTTP sessions?

  • 您想使用 WebSocket 吗?

  • Do you want to use WebSockets?

  • 您想使用 Maven 还是 Gradle?

  • Would you like to use Maven or Gradle?

  • 您想使用 Grunt 或 Gulp.js 来构建前端吗?

  • Would you like to use Grunt or Gulp.js for building the front end?

  • 您想使用 Compass CSS 创作框架吗?

  • Would you like to use the Compass CSS Authoring Framework?

  • 您想启用 Angular Translate 的翻译支持吗?

  • Would you like to enable translation support with Angular Translate?

对于每个问题,都有一个清晰的叙述,解释可能的答案和后果,以帮助做出决定。这也是内联的、定制的帮助。生成的代码是所有决策的结果。如果您选择 MySQL 作为数据库,那么您就拥有了 MySQL 数据库设置。

For each question, there is a clear narrative explaining the possible answers and the consequences to help make the decision. This is also inline, tailored help. The resulting code is the result of all the decisions. If you’ve chosen MySQL as the database, then you have a MySQL database setup.

将向导所有问题的响应记录到一个文件中(它们仅作为日志或控制台保存)以提供应用程序的高级技术概述会很有趣。例如,它可能包含在自述文件中。

It would be interesting to record the responses to all the questions of the wizard into a file (they’re only kept as logs or in the console) to provide a high-level technical overview of the application. It might be included in the README file, for example.

向导应该设计有用的异常,准确地告诉您解决什么问题、如何解决以及在哪里解决引发的问题。

A wizard should design helpful exceptions that precisely tell you what, how, and where to fix a problem that is thrown.

机器文档

Machine Documentation

在云出现之前,我们必须一一了解我们的机器,并且经常有一个 Excel 电子表格,其中包含机器列表及其主要属性。这个列表经常是过时的。

Before the cloud, we had to know our machines one by one, and there was often an Excel spreadsheet somewhere with a list of machines and their main attributes. This list was often obsolete.

现在,机器正在移动到云中的某个地方,我们不再能够保留电子表格,因为信息变化太频繁,有时一天会变化很多次。但由于云本身是自动化的,现在可以通过云 API 免费提供非常准确的文档。

Now that machines are moving somewhere in the cloud, we can no longer afford to keep a spreadsheet, as the information changes much too frequently, sometimes many times a day. But because the cloud itself is automated, very accurate documentation now comes for free, through the cloud API.

云 API 类似于声明式自动化。您声明您想要什么,例如“我想要一个带有 Apache 的 Linux 服务器”,然后您可以查询当前可用计算机的清单及其所有属性。其中许多属性是标签和元数据,它们为图片添加了更高级别的信息:例如,也许它不是“2.6GHz Intel Xeon E5”,但它是“高 CPU 机器”。

The cloud API is similar to declarative automation. You declare what you want, such as “I want a Linux server with Apache,” and then you can query your current inventory of machines available and all their attributes. Many of these attributes are tags and metadata that add a higher level of information to the picture: Perhaps it’s not a “2.6GHz Intel Xeon E5,” but it’s a “High-CPU machine,” for example.

关于自动化的一般评论

Remarks on Automation in General

不要做两次同样的事情。如果你有似曾相识的感觉,那么就该实现自动化了。

Don’t do the same thing twice. If it’s déjà -vu, then it’s time to automate.

——伍迪·祖尔,对话中

—Woody Zuill, in a conversation

人们喜欢新奇的东西。机器适合重复的事情。自动化带来了好处,但也付出了代价。它本身并不是目的,而是节省时间和提高重复任务可靠性的一种手段。但总有一个点是成本超过收益。只要与经常性收益相比成本较低,您就应该投资自动化。

People are good for novel stuff. Machines are good for repeated stuff. Automation provides benefits—but at a cost. It is not an end in itself but a mean to save time and to improve reliability on repeated tasks. But there is always a point where the cost exceeds the benefits. You should invest in automation as long as the cost is low compared to the recurring benefits.

另一方面,如果任务每次都是新的或不同的,您应该等到在任务中的某个地方看到足够的重复后再考虑自动化。决定保留哪些手册。

On the other hand, if a task is new or different each time, you should wait until you see enough repetition somewhere in the task before thinking about automation. Decide what to keep manual.

强制准则

Enforced Guidelines

如果最好的文档能够在正确的时间用正确的知识提醒您,那么甚至不需要阅读它。

The best documentation does not even have to be read, if it can alert you at the right time with the right piece of knowledge.

仅仅提供信息是不够的。没有人能够提前阅读并记住所有可能的知识。您需要很多知识,但没有任何方法可以确定您是否需要它们。

Making information available is not enough. Nobody can read and remember all the possible knowledge ahead of time. And there is a lot of knowledge that you’d need without having any way to figure out that you need it.

笔记

Note

你甚至不知道你不知道一些你应该知道的事情。

You don’t even know that you don’t know something that you should know.

考虑代码指南。许多公司和团队花时间编写指南,但最终的文档很少被阅读,而且经常被忽视。

Consider code guidelines. Many companies and teams spend time writing guidelines, but the resulting documents are seldom read and often ignored.

您如何记录已做出的所有决定以及每个人在工作时都应遵守的决定?这些决策的示例包括主要架构决策、编码指南以及有关风格和团队偏好的其他决策。

How do you document all the decisions that have been made and that everybody should conform to when doing their work? Examples of these decisions include the main architectural decisions, coding guidelines, and other decisions about style and team preferences.

一种常见的方法是花时间将这些决定写入指南或样书。问题在于,这些决定很快就会增加比您预期更多的页数,而一份充满“您应该这样做”和“您不应该这样做”的 12 页长的文档读起来远非令人兴奋。因此,这些文档中的大多数都像法律文档:它们非常无聊,以至于大多数团队成员从未阅读过它们,甚至一次也没有。他们假装在到达时已经阅读了这些内容,但实际上他们几乎只读了第二页或第三页。

A common approach is to spend time writing these decisions into a guideline or style book. The problem is that these decisions quickly add up to more pages than you expected, and a 12-page-long document full of “you shall do this” and “you shall not do that” is far from an exciting read. As a consequence, most of these documents are like legal documents: They are so boring that most team members never read them—even once. They pretend to have read them on arrival, but in fact they hardly went further than the second or third page.

即使他们真正阅读了它们,规则列表的格式也不容易记住,除非您喜欢所有规则,否则您不会记住其中的大多数规则。在实践中,这些指南在出现疑问时可作为参考,仅此而已。

Even when they’ve actually read them, the format as a list of rules is not memorable, and unless you like all the rules, you won’t remember most of them. In practice, these guides are useful as a reference in case of doubt, and not much more.

然而,如果没有指导方针,代码就会受到每个人自己的风格、偏好或缺乏技能的支配。一套一致的共享准则对于真正以集体所有制方式开展工作至关重要。

However, without guidelines, code is at the mercy of everybody’s own style, preferences or lack of skills. A consistent set of shared guidelines is essential to really work in a collective ownership fashion.

那么人们如何了解他们必须真正遵守的所有决定和风格呢?他们通过阅读其他人的代码、通过代码审查以及通过捕获规则违规的静态分析工具的反馈来了解所有这些。

So how do people learn about all the decisions and style they have to conform to for real? They learn all that by reading other people’s code, through code reviews, and through feedback from static analysis tools that catch rule violations.

当代码具有示范性时,阅读代码效果很好,但情况并非总是如此。当然,代码审查和静态分析有助于改善这一点。代码审查工作只要审稿人考虑到所有决定和风格偏好并且都同意它们。静态分析适用于不需要细微差别或上下文解释的每个规则或决策。而且由于静态分析工具必须经过配置才能发挥作用,一旦配置完毕,它们本身自然就是所有指南的参考文档。

Reading code works well when the code is exemplary, which is not always the case. Of course, code review and static analysis help improve that. Code review works well as long as the reviewers have all the decisions and style preferences in mind and all agree with them. Static analysis works well for every rule or decision that doesn’t need nuance or contextual interpretation. And because static analysis tools must be configured to be useful, once configured, they are themselves naturally the reference documentation about all the guidelines.

因此:使用一种机制来执行已制定为指导方针的决定。使用工具检测违规行为并以可见警报的形式提供违规行为的即时反馈。不要浪费时间编写没人读的指导文件。相反,使执行机制具有足够的自我描述性,以便可以用作指南的参考文档。

Therefore: Use a mechanism to enforce the decisions that have been made into guidelines. Use tools to detect violations and provide instant feedback on violations as visible alerts. Don’t waste time writing guideline documents that nobody reads. Instead, make the enforcement mechanism self-descriptive enough so that it can be used as the reference documentation of the guidelines.

代码分析工具有助于在代码中的任何地方保持高水平的质量,这反过来又有助于代码成为典范。当程序员在代码审查或结对编程期间对规则犹豫不决时,它也可以作为参考。

Code analysis tools help maintain a high level of quality anywhere in the code, which in turn helps the code to be exemplary. And it also helps as a reference when the programmers are hesitant about a rule during a code review or while pair programming.

强制执行指南的要点是承认文档甚至不必阅读即可发挥作用。最好的文档在正确的时间(当您需要时)为您带来正确的知识。通过工具(或代码审查)强制执行规则、属性和决策是一种在团队成员忽略时准确地教授他们所需知识的方法。

The point of enforced guidelines is to accept that documentation does not even have to be read to be useful. The best documentation brings you the right piece of knowledge at the right time—when you need it. Enforcing rules, properties, and decisions through tools (or code reviews) is a way to teach the team members the knowledge they need precisely at the moment they ignore it.

笔记

Note

强制执行的指导方针提供了持久的知识,使其再次具有互动性。

Enforced guidelines provide persistent knowledge made interactive again.

一些规则示例

Some Examples of Rules

修饰规则有助于代码一致性以及合并代码。以下是示例:

Cosmetic rules help with code consistency and when merging code. The following are examples:

  • 大括号不得省略。

  • Curly brackets must not be omitted.

  • 字段名称不得使用匈牙利表示法。

  • Field names must not use Hungarian notation.

指标规则有助于阻止过于复杂的代码。以下是一些示例:

Rules on metrics help discourage overly complicated code. The following are some examples:

  • 避免深度继承树(最大值 = 5)。

  • Avoid deep inheritance trees (max = 5).

  • 避免复杂的方法(最多 = 13)。

  • Avoid complex methods (max = 13).

  • 避免行过长(最多 = 120 个字符)。

  • Avoid overly long lines (max = 120 chars).

规则提供了一种鼓励或强制执行更好代码的方法。以下是一些示例:

Rules provide a way to encourage or enforce better code. The following are some examples:

  • 不要破坏堆栈跟踪。

  • Do not destroy the stack trace.

  • 例外情况应该是公开的。

  • Exceptions should be public.

有些规则可以直接避免bug:

Some rules can directly avoid bugs:

  • ImplementEqualsAndGetHashCodeInPairRule

  • ImplementEqualsAndGetHashCodeInPairRule

  • 正确测试 NaN。

  • Test for NaN correctly.

甚至一些架构决策也可以作为规则制定。考虑这些例子:

Even some architectural decisions can be made as rules. Consider these examples:

  • DomainModelElementsMustNotDependOnInfrastructure

  • DomainModelElementsMustNotDependOnInfrastructure

  • ValueObjectMustNotDependOnServices

  • ValueObjectMustNotDependOnServices

然后您可以在规则之上添加一些游戏化,如图10.9所示。

Then you can add some gamification on top of the rules, as illustrated in Figure 10.9.

代表“指导方针执行力量”的人物展示了两个卡通人物,其中一个卡通人物写着“你声明了六角形架构。但是我发现了从你的域到 MongoDB 的流氓依赖!”,这是 25 美元。或者一杯咖啡为了团队中的每个人!”  。

图10.9 指南执行力

Figure 10.9 The guidelines enforcement force

改进指南

Evolving the Guidelines

指南有其目的,例如帮助团队协同工作、减少合并代码时的错误或错误等问题,以及保留性能和可维护性等质量属性。不存在一套理想且明确的指导方针。相反,您应该从一些指导原则开始,使用它们,并改进它们以使其尽可能相关。

Guidelines have a purpose, such as helping a team work together, reducing issues like bugs or errors when merging code, and preserving quality attributes such as performance and maintainability. There’s no such a thing as an ideal and definitive set of guidelines. Instead, you should start with some guidelines, use them, and evolve them to make them as relevant as possible.

最好的指导方针不是来自上面。最好的指导方针来自于一个或多个团队的工作和相互交谈,以就有用的共享指导方针达成一致。必要时请毫不犹豫地更改指南。当然,您可能不想每天都改变代码行的长度。

The best guidelines don’t come from above. The best guidelines grow from the team or teams doing work and talking to each other to agree on shared guidelines that are useful. Don’t hesitate to change the guidelines when necessary. Of course, you may not want to change the length of lines of code every day.

以下是新建项目的示例指南列表:

The following is a list of sample guidelines for a greenfield project:

  • 单元测试覆盖率 >80%

  • Unit test coverage >80%

  • 方法复杂度<5

  • Complexity by method <5

  • 方法 LOC < 25

  • LOC by method < 25

  • 继承深度 < 5

  • Inheritance depth < 5

  • 参数数量 < 5

  • Number of args < 5

  • 成员数据字段数量 < 5

  • Number of member data fields < 5

  • 所有基本 Checkstyle 规则

  • All base Checkstyle rules

强制执行或鼓励

Enforcement or Encouragement

在一个新建项目中,您通常会以严格的方式开始执行许多强制准则,并且违反这些准则的每一行新代码都会被拒绝。另一方面,在遗留项目中,您通常无法这样做,因为现有代码可能已经包含数千个违规行为,即使对于小模块也是如此。相反,您可以选择仅强制执行少数最重要的准则,并将所有其他准则设为警告。另一种方法是仅对新代码行制定更严格的规则。

On a greenfield project, you typically start with a lot of enforced guidelines in a strict fashion, and every new line of code that violates them has its commit rejected. Other the other hand, on a legacy project, you usually can’t do this because the existing code likely already contains thousands of violations, even for a small module. Instead, you might choose to enforce only the few most important guidelines and make every other guideline a warning. Another approach is to have stricter rules only for new lines of code.

有些团队从一些指导方针开始,当他们对这些指导方针感到满意时,他们会添加更多规则并使现有指导方针更加严格,以便取得进展。

Some teams start with some guidelines, and when they are comfortable with them, they add more rules and make the existing guidelines stricter in order to progress.

当您的公司要求每个应用程序遵循一套最低限度的准则时,每个团队或应用程序都可以决定使其更严格,但不能更弱。像 Sonar 这样的工具提供了多组指南之间的继承,称为质量配置文件,以帮助做到这一点。您可以定义一个配置文件来扩展公司配置文件并添加更多规则或使现有规则更严格以满足您自己的口味。

When your company requires every application to follow a minimum set of guidelines, each team or application can decide to make it stricter but not weaker. Tools like Sonar provide inheritance between sets of guidelines, called quality profiles, to help do that. You can define a profile that extends the company profile and add more rules or make the existing rules stricter to suit your own taste.

声明性指南

Declarative Guidelines

由于可以命名多套指南或质量概况,因此它们的名称也是指南文档的一部分。您可以简单地让新加入者参考构建配置,他们将在其中找到指南集的名称。从那里,他们可以在该工具上进行查找,并发现它扩展了公司的指导方针。他们可以按类别或严重性浏览规则,并以交互方式根据需要检查参数。甚至还有一个搜索引擎。

Because sets of guidelines, or quality profiles, can be named, their names are also part of the documentation on guidelines. You can simply refer new joiners to the build configuration, where they will find the name of the set of guidelines. From there, they can look it up on the tool and find out that it extends the company sets of guidelines. They can browse the rules by category or severity and check parameters as they wish, in an interactive fashion. There’s even a search engine.

每个给定的规则都有一个关键字、一个标题以及其内容和原因的简要描述。通过密钥或标题,您可以在该工具上或直接在网络上查找更完整的文档。

Each given rule has a key, a title, and a brief description of what it is and why. With the key or the title, you can look up more complete documentation on the tool or directly on the web.

ImplementEqualsAndGetHashCodeInPairRule例如,如果您在网络上查找,您会立即从 .Net 的 Gendarme 插件中找到其参考文档:

For example, if you look up ImplementEqualsAndGetHashCodeInPairRule on the web, you immediately find its reference documentation, from the Gendarme plugin for .Net:

Equals(object)此规则检查重写方法但不重写GetHashCode()或重写GetHashCode但不重写的类型Equals。为了正确工作,类型应该始终一起覆盖它们。26

This rule checks for types that either override the Equals(object) method without overriding GetHashCode() or override GetHashCode without overriding Equals. In order to work correctly types should always override these together.26

26. https://www.mono-project.com/docs/tools+libraries/tools/gendarme/rules/design/#implementequalsandgethashcodeinpairrule

26.https://www.mono-project.com/docs/tools+libraries/tools/gendarme/rules/design/#implementequalsandgethashcodeinpairrule

此类参考文档通常包括几个代码示例、一个不好的示例和一个说明规则要点的好示例。这太棒了,因为文档已经存在了。既然别人已经写得很好了,为什么还要再写一遍呢?

Such reference documentation usually includes several code samples, a bad example, and a good example to illustrate the point of the rule. This is great, because the documentation is already there. Why write it again when it has been already done well by someone else?

工具问题

A Matter of Tools

编译器、代码覆盖率、静态代码分析工具、错误检测器、重复检测器和依赖性检查器是在实践中设置强制指南的工具的常见示例。

Compilers, code coverage, static code analysis tools, bug detectors, duplication detectors, and dependency checkers are common examples of tools for setting up enforced guidelines in practice.

声纳是一种流行的工具,它本身依赖于许多插件来实际完成其工作。当工具的配置不意味着成为带有详细 XML 和规则标识符的文档时,像 Sonar 这样的工具可以使编码规则的配置在方便的 UI 中更容易访问,甚至成为指南的参考。

Sonar is a popular tool that itself relies on many plugins to actually do its job. When the configuration of tools is not meant to be documentation with verbose XML and rules identifiers, tools like Sonar can make the configurations of coding rules more accessible in a convenient UI, to the point of becoming the reference about guidelines.

即使插件实际上是通过 XML 文件配置的,Sonar 也会在屏幕上很好地显示编码规则列表,您可以在那里修改它们以及散文中的参考描述。该信息还可以以电子表格格式导出。如果您确实想花时间手动记录编码指南,只需给出总体意图、优先级和偏好,然后让工具提供详细信息!

Even when plugins are actually configured via an XML file, Sonar displays the list of coding rules nicely onscreen, and you can modify them there, along with the reference description in prose. This information can also be exported in a spreadsheet format. If you really want to spend time documenting coding guidelines manually, just give the overall intentions, priorities, and preferences and let the tools provide the details!

其他准则可以通过访问控制来强制执行。假设您决定从现在开始冻结某个遗留组件,并且没有人有权对其进行承诺。您可以简单地撤销对每个人的写入授权。但这本身并不解释你为什么做出这个决定。因此,您应该期待问题,并且知识转移将以对话的形式进行。

Other guidelines may be enforced by access control. Say that you have decided that a legacy component is frozen from now on, and nobody has the right to commit on it. You can simply revoke the write grants to everyone. But this in itself does not explain why you’re making the decision. You should therefore expect questions, and the knowledge transfer will happen as a conversation.

大多数自动化手段在任何时候都不是 100% 相关,因此有时会违反执行规定。这并不一定是一场灾难,只要执法部门对指南保持足够的持续认识即可。

Most automated means are not 100% relevant at any time, so sometimes enforcement will be violated. This is not necessarily a disaster, as long as the enforcement maintains enough continuous awareness about the guidelines.

如果指南的某个要素不可强制执行,那么它可能就不是真正的指南要素。您可能希望将其添加到简短的清单中,以便在结对编程期间进行手动代码审查或审查。但它不再是强制的指导方针。

If an element of the guideline is not enforceable, then perhaps it is not really an element of a guideline. You might want to add it to a short checklist for manual code review or review during pair programming. But it is not an enforced guideline any longer.

但是,如果您有新规则,您可以考虑使用新规则或新插件扩展现有工具。编译器通常有扩展点,您可以在其中挂钩自己的附加规则。像 Sonar 这样的工具可以通过自定义插件进行扩展,检查器可以通过新规则进行扩展,有时使用 XML,有时仅使用代码。

However, if you have new rules, you may consider extending the existing tools with a new rule or new plugin. Compilers often have extension points where you can hook your own additional rules. Tools like Sonar are extensible with custom plugins, and checkers are extensible with new rules, sometimes with XML and sometime only with code.

指南或设计文档?

Guidelines or Design Documentation?

想象一下,您的领域模型指南如下:

Imagine that your set of guidelines for the domain model is as follows:

  • 功能第一(默认情况下不可变且无副作用)

  • Functional first (immutable and side effect free by default)

  • 无空值

  • Null free

  • 无框架污染

  • No framework pollution

  • 没有SQL

  • No SQL

  • 不直接使用日志框架

  • No direct use of a logging framework

  • 不进口任何基础设施技术

  • No import of any infrastructure technology

在撰写本文时,现有的静态分析工具和插件并不支持所有这些开箱即用的功能,因此除非您创建自己的工具,否则您无法执行强制指南。然而,这些指导原则是可以记录在代码本身中的设计决策,也许可以通过使用注释来记录,如第 4 章“知识增强”中所述。

At the time of this writing, existing static analysis tools and plugins don’t support all this out of the box, so you can’t do enforced guidelines unless you create your own tool. However, these guidelines are design decisions that can be documented in the code itself, perhaps by using annotations, as discussed in Chapter 4, “Knowledge Augmentation.”

事实上,这种以注释形式表达的设计声明可以使用分析工具强制执行您的编码标准和其他准则。一旦你声明你的代码在给定的包中应该是不可变的,就可以使用解析器检查主要的违规行为。

In fact, such design declarations expressed as annotations make it possible to enforce your coding standards and other guidelines with analysis tools. Once you declare that your code should be all immutable in a given package, it becomes possible to check the main violations using a parser.

不变性和无 null 期望可以通过编程方式强制执行。这远非完美,但足以让任何新加入者在几次提交后学习这种风格。

Immutability and null-free expectations can be enforced programmatically. This is far from perfect, but it is enough for any new joiner to learn the style after a few commits.

保修贴纸如果被篡改则无效

Warranty Sticker Void if Tampered With

Hamcrest 27是一个流行的开源项目,它提供匹配器来编写漂亮的单元测试。它提供了许多开箱即用的匹配器,您还可以使用自己的自定义匹配器对其进行扩展。通常,当您这样做时,您应该阅读开发人员指南,但并不是每个人都会这样做。因此,Hamcrest 以创造性的方式使用命名,使其不太可能因无知而破坏设计决策:

Hamcrest27 is a popular open-source project that provides matchers to write beautiful unit tests. It provides a lot of matchers out of the box, and you can also extend it with your own custom matchers. Usually when you do that you should read the developer’s guide, but not everyone does that. Therefore, Hamcrest uses naming in a creative way to make it very unlikely to break a design decision by ignorance:

27.汉克雷斯特, http://hamcrest.org

27.Hamcrest, http://hamcrest.org

1   /** 
2   * 这个方法只是友情提醒不要
直接实现\ 
3   * Matcher并扩展BaseMatcher。
4   * 忽略 JavaDoc很容易,但忽略编译则有点困难
错误
5 *
6 *
7 *
8 * @see Matcher 了解原因。
9 * @see BaseMatcher 
10 * @deprecated to make
11*/
12 @已弃用
13 _ void _dont_implement_Matcher___instead_extend BaseMatcher_();
1  /**
2  * This method simply acts a friendly reminder not to
implement\
3  * Matcher directly and instead extend BaseMatcher. It's easy
4  * to ignore JavaDoc, but a bit harder to ignore compile
errors
5  *
6  *
7  *
8  * @see Matcher for reasons why.
9  * @see BaseMatcher
10 * @deprecated to make
11 */
12  @Deprecated
13 _ void _dont_implement_Matcher___instead_extend BaseMatcher_();

HamcrestMatcher方法不实现匹配器,而是实现 extends Base Matcher,这是一种不可能错过且无用的文档方法。你仍然可以故意破坏它,但关键是你要意识到这样做。这是一种“如果被篡改,保修无效”的贴纸。这是进行不可避免的文档记录的原始方法。

The Hamcrest Matcher method doesn’t implement a matcher but extends Base Matcher, which is an impossible-to-miss and otherwise useless documentation method. You can still break it deliberately, but the point is that you’re aware of doing that. It’s a kind of “warranty void if tampered” sticker. This is an original way to do unavoidable documentation.

有趣的是,在这个强制执行指南的例子中,执行是由潜在的违规者自己完成的。

The funny things is that in this example of enforced guidelines, the enforcement is done by the potential violator himself or herself.

以下是一些更类似的例子:

The following are some more similar examples:

  • 例外文档:假设您决定将旧组件从读写变为只读。您可以使用文本或注释来记录这一点,但如何确保没有人会添加写入行为?一种方法是在所有数据访问对象上保留 write 方法,但让它们抛出异常,即使用IllegalAccessException("The component is now READ-ONLY").

  • Documentation by exception: Say that you decide to turn a legacy component from read-write to read-only. You can document this with text or annotations, but how can you make sure nobody will add write behavior? One way is to keep the write methods on all the data access objects but have them throw exceptions is to use IllegalAccessException("The component is now READ-ONLY").

  • 许可机制:您可以创建一个模块,除了某个特定项目之外,任何人都不应导入该模块,并且您无法在包管理器本身中执行此操作。您可以实现一个非常简单的许可证机制:当您导入模块时,它会抛出异常,抱怨它缺少许可证文本文件或许可证ENV变量。许可证可以是诸如“我不应该导入此模块”之类的文本,作为免责声明。您可以破解它,但如果您这样做,您就接受免责声明!

  • License mechanism: You can create a module that nobody should import except into one particular project, and you have no way to do that within the package manager itself. You can implement a very simple license mechanism: When you import the module, it throws exceptions, complaining that it’s missing a license text file or license ENV variable. The license can be text such as “I should not import this module” acting as a disclaimer. You can hack it, but if you do, you accept the disclaimer!

信任第一的文化

Trust-First Culture

将指导方针作为自动规则或通过访问限制来执行可能会表达对团队缺乏信任,但这在很大程度上取决于您的公司文化。如果你的文化确实是一种每个人之间信任、自主和责任的文化,那么引入强制指导方针应该在每个相关人员讨论后通过共识决定。在最坏的情况下,引入强制指导方针可能会发出错误的信号并破坏信任,这将是比您所追求的利益更大的损失。

Enforcing guidelines as automated rules or through access restrictions may express a lack of trust to the teams, but it depends a lot on your company culture. If your culture really is a culture of trust, autonomy, and responsibility between everyone, then introducing enforced guidelines should be decided by consensus after discussions between everyone involved. In the worst case, introducing enforced guidelines could send the wrong signal and undermine trust, which would be a greater loss than the benefits you’re after.

行为受限

Constrained Behavior

您可以影响或限制行为,而不是记录下来。执行指南并不是在正确的时间向开发人员提供正确的知识的唯一方法;一个有趣的选择是首先影响或约束他们做正确的事情,而他们不一定意识到这一点。

Rather than document, you can influence or constrain the behavior instead. Enforcing guidelines is not the only approach to bring the right piece of knowledge at the right time to the developers; an interesting alternative is to influence or constrain them to do the right thing in the first place, without them being necessarily aware of it.

轻松做正确的事

Making It Easy to Do the Right Thing

例如,您可以决定“从现在开始,开发人员必须创建更多模块化代码,因为必须单独部署新的小型服务。” 您甚至可以将其打印在指南文件上,并希望每个人都能阅读并遵循此决定。

For example, you could decide that “from now on, developers MUST create more modular code, as new small services that MUST be deployed individually.” You could even print this on the guidelines document and hope everyone will read it and follow this decision.

或者你可以投资改变环境:

Or you could invest in changing the environment:

  • 提供良好的自助式 CI/CD 工具:通过轻松设置新的构建和部署管道,开发人员更有可能创建新的单独模块,而不是将所有新代码放入同一个大泥球中。他们知道如何构建和部署。

  • Provide good self-service CI/CD tools: By making it easy to set up a new build and deployment pipeline, you make it more likely that developers will create new separate modules rather than put all new code into the same big ball of mud that they know how to build and deploy.

  • 提供良好的微服务框架(请参阅 Chris Richardson 的网站,https://microservices.io):您可以通过轻松引导新的微服务来鼓励模块化,而无需花费时间将所有必要的库和框架连接在一起。

  • Provide a good microservices chassis (see Chris Richardson’s website, https://microservices.io): You can encourage modularity by making it easy to bootstrap a new microservice without spending time wiring together all the necessary libraries and frameworks.

Sam Newman在他的《构建微服务》一书中写道,通过他所谓的定制服务模板,可以轻松地做正确的事情:

In his book Building Microservices, Sam Newman writes on making it easy to do the right thing, with what he calls tailored service templates:

如果您可以让所有开发人员只需很少的工作就能轻松遵循您的大部分指南,这不是很好吗?如果开发人员开箱即用地拥有大部分代码来实现每个服务所需的核心属性,该怎么办?

Wouldn’t it be great if you could make it easy for all developers to follow most of the guidelines you have with very little work? What if, out of the box, the developers had most of the code in place to implement the core attributes that each service needs?

……

例如,您可能想要强制使用断路器。在这种情况下,您可以集成像 Hystrix 这样的断路器库。或者,您可能有这样的做法:所有指标都需要发送到中央开源库(例如 Dropwizard 的指标)并对其进行配置,以便开箱即用地将响应时间和错误率自动推送到已知位置。28

For example, you might want to mandate the use of circuit breakers. In that case, you might integrate a circuit breaker library like Hystrix. Or you might have a practice that all your metrics need to be sent to a central an open source library like Dropwizard’s Metrics and configure it so that, out of the box, response times and error rates are pushed automatically to a known location.28

28.纽曼,山姆。构建微服务。加利福尼亚州塞瓦斯托波尔:O'Reilly Media, Inc.,2015。

28.Newman, Sam. Building Microservices. Sebastopol, CA: O’Reilly Media, Inc., 2015.

最著名的科技公司通过您也可以使用的开源库来采用这种方法。用萨姆·纽曼的话来说:

The most famous tech companies embrace this approach with open-source libraries that you, too, can use. In the words of Sam Newman:

例如,Netflix 特别关注容错等方面,以确保其系统的某一部分的中断不会导致所有系统瘫痪。为了解决这个问题,我们做了大量的工作来确保 JVM 上有客户端库,为团队提供保持服务良好运行所需的工具。

Netflix, for example, is especially concerned with aspects like fault tolerance, to ensure that the outage of one part of its system cannot take everything down. To handle this, a large amount of work has been done to ensure that there are client libraries on the JVM to provide teams with the tools they need to keep their services well behaved.

环境也在传递信息。它是隐性的、被动的,我们通常不会注意到这一点。您可以通过将环境中阻力最小的路径设计为您喜欢的路径来深思熟虑并决定传递什么信息。

The environment is also passing information. It’s implicit and passive, and we don’t often pay attention to that. You can make it deliberate and decide what message to pass by designing the path of least resistance in the environment to be the one that you favor.

更一般地说,您不仅想让行为变得更容易,而且更有价值。通过将提交历史记录显示为精美的像素艺术图,GitHub 使经常提交成为一种有益的事情。开发商的自豪感太强大了!

More generally, you want to make behavior not just easier but also more rewarding. By showing the commit history as a nice pixel art diagram, GitHub makes it a rewarding thing to commit often. Developers’ pride is powerful!

本书所提倡的活文档的一个要点是提供简单的文档方法,以鼓励更多地进行文档记录。

A major point of living documentation in general as advocated in this book is to offer simple ways to document to encourage doing it more.

让错误成为不可能:防错 API

Making Mistakes Impossible: Error-Proof API

以一种不可能被滥用的方式设计您的 API。这减少了对文档的需求,因为没有什么可以警告用户的。

Design your API in a way that makes it impossible to misuse. This reduces the need for documentation, since there’s nothing to warn the user about.

Michael L. Perry 在博客文章中列出了许多常见的 API 陷阱:

Michael L. Perry lists many common API traps in a blog post:

  • 调用此方法之前必须设置一个属性。

  • You must set a property before calling this method.

  • 调用此方法之前必须检查条件。

  • You must check a condition before calling this method.

  • 设置属性后必须调用此方法。

  • You must call this method after setting properties.

  • 调用方法后无法更改此属性。

  • You cannot change this property after calling a method.

  • 此步骤必须发生在该步骤之前。29

  • This step must occur before that step.29

29. Michael L. Perry,QED 代码博客, http://qedcode.com/practice/provable-apis.html

29.Michael L. Perry, QED Code blog, http://qedcode.com/practice/provable-apis.html

这些陷阱不应该被记录下来;相反,它们应该被重构以被删除!否则,文档将成为可耻评论的绝佳案例。

These traps should not be documented; instead, they should be refactored to be removed! Otherwise, the documentation will be a great case of shameful comment.

有无数种方法可以使 API 不会被滥用,包括以下几种:

There are endless ways to make an API impossible to misuse, including the following:

  • 仅使用类型来公开您可以实际以任何顺序调用的方法。

  • Use types only to expose methods you can actually call, in any order.

  • 使用枚举来枚举每个有效的选择。

  • Use enums to enumerate every valid choice.

  • 在实际使用无效属性之前尽早检测它们(例如,直接在构造函数中捕获无效输入),然后尽可能进行修复,例如在构造函数或 setter 中用 null 对象替换 null。

  • Detect invalid properties as early as possible (for example, catch invalid inputs directly in the constructor), well before they are actually used, and then repair whenever possible, such as replacing nulls with null objects in the constructors or setters.

  • 这不仅与错误有关,还与任何有害的幼稚用法有关。例如,如果一个类可能用作哈希映射中的键,那么它不应该使哈希映射变慢或不一致。hashcode()您可以使用内部缓存来记住和的任何缓慢计算的结果toString()

  • It’s not just about errors but also about any harmful naive usage. For example, if a class is likely to be used as the key in a hashmap, it should not make the hashmap slow or inconsistent. You could use internal caches to memorize the results of any slow computations of hashcode() and toString().

一个常见的反对意见是,经验丰富的开发人员不会犯这些简单的错误,因此没有必要如此防御。然而,即使是优秀的开发人员也有比避免 API 陷阱更重要的事情需要关注。

A common objection is that experienced developers don’t make these easy mistakes, so there is no need to be so defensive. However, even good developers have more important things to focus on than avoiding the traps of your API.

唐·诺曼 (Don Norman) 呼吁就如何指导某些事物的使用提供建议。30

Don Norman calls advice on how to guide the use of something affordances.30

30. Norman, Donald A.日常事物的设计。纽约:Basic Books, Inc.,2002 年。

30.Norman, Donald A. The Design of Everyday Things. New York: Basic Books, Inc., 2002.

避免文档的设计原则

Design Principles for Documentation Avoidance

在 QCon 2015 期间,Dan North 谈到了一种模型,在该模型中,代码要么非常古老且完善,以至于每个人都知道如何处理它,要么它非常年轻,以至于每个人都知道如何处理它。做这件事的人还在那里,所以他们知道这一切。当你处于这两个极端之间的灰色地带时,就会出现问题。

During QCon 2015, Dan North talked about a model in which code is either so old and well established that everybody knows how to deal with it, or it’s so young that the people doing it are still there, so they know all about it. Problems happen when you’re in a gray zone between these two extremes.

Dan 强调知识共享和知识保存的核心作用,这是成功团队的关键要素。他进一步提出了解决这个问题的替代方法。

Dan emphasizes the central role of knowledge sharing and knowledge preservation as a key ingredient of successful teams. He goes further, suggesting alternative ways to deal with this issue.

可替换性第一

Replaceability First

可替换性设计减少了了解事物如何工作的需要。对于可以轻松更换的组件,您不需要太多文档。当然,您需要知道组件在做什么,但您不必知道它们是如何做的。

Designing for replaceability reduces the need to know how things work. You don’t need much documentation for components you can replace easily. Sure, you need to know what the components were doing, but you don’t have to know how they were doing it.

在这种心态下,您可以放弃维护。如果你必须改变某些东西,你可以全部重建。为了使这种方法发挥作用,每个部分都必须相当小,并且尽可能独立于其他每个组件。这将注意力转移到组件之间的契约上。

In this mindset, you could give up maintenance. If you must change something, you could just rebuild it all. For this approach to work, every part has to be reasonably small and as independent as possible from every other component. This shifts the attention to the contracts that are between components.

因此: 青睐能够轻松更换整体中的某个部件的设计。确保每个人都清楚该部件的作用。否则,您需要行为的文档 - 例如,您可以轻松使用的工作软件、输入和输出的自记录合同或自动化且可读的测试。

Therefore: Favor a design that makes it easy to replace a part within the whole. Make sure that everybody knows exactly what the part does. Otherwise, you need documentation for the behavior—for example, the working software you can easily play with, self-documented contracts of the inputs and outputs, or automated and readable tests.

当团队对设计不够关心时,组件就会变得越来越多。他们很快就与一切融为一体。因此,你永远无法真正完全取代它们。让代码易于替换仍然是一种设计行为;它的发生并非纯粹出于运气,也不是没有技巧和细心。这需要纪律。一种明显的方法是限制组件的大小,例如屏幕上最多一页。另一种方法是对哪些组件可以相互调用进行严格限制,并防止它们共享数据存储。

When the team does not care enough about design, the components grow and get hairy. They quickly get coupled to everything. As a result, you can never really replace them totally. Making the code easy to replace is still an act of design; it does not happen out of pure luck or without skills and care. It takes discipline. One obvious way is to limit the size of the component—for example, up to one page on the screen. Another way is to create strict restrictions on what components can call each other and preventing them from sharing data storage.

即使采用有利于可替换性的方法,设计技能仍然是必要的。例如,开放/封闭原则确实是使实现易于替换的一个例子;另一个是它的好朋友里氏替换原则。其他坚实的原则也有帮助。它们通常在类和接口级别进行讨论。但它们也适用于组件或服务级别。但要真正以低成本进行替代,它们必须很小——这就是微服务的想法。

Even with an approach that favors replaceability, design skills remain necessary. For example, the open/closed principle is indeed a case of making the implementation replaceable easily; another is its good friend the Liskov substitution principle. Other solid principles also help. They are usually discussed at the class and interface levels. But they also apply at the level of components or services. But to be really replaceable at low cost, they have to be small—hence the idea of microservices.

一致性第一

Consistency First

代码库的一致性是指您从未见过的代码看起来很熟悉,以便您可以轻松处理它。

Consistency in the code base is when code that you’ve never seen looks familiar so that you can deal with it easily.

—Dan North 在 2015 年伦敦 QCon 大会上31

—Dan North at QCon London 201531

31. https://qconlondon.com/london-2015/speakers/dan-north.html

31.https://qconlondon.com/london-2015/speakers/dan-north.html

保持一致可以减少对文档的需求。在实践中,超出界限的区域很难保持一致性;在一个组件内、在一种编程语言内、甚至在一层内,一致性更加自然。GUI 逻辑的编程风格通常与服务器端域逻辑的编程风格不同。

Being consistent reduces the need for documentation. In practice, consistency is hard to maintain beyond bounded areas; consistency is more natural within one component, within one programming language, and even within one layer. You often don’t follow the same programming style for GUI logic as for server-side domain logic.

对于具有一致代码风格的代码库的给定区域,一旦了解了该风格,就对该区域中的所有元素没有什么可说的。一致性使一切都标准化。一旦你知道了标准,就没有什么可说的了。

For a given area of the code base with a consistent style of code, once you know the style, there’s nothing more to say for all elements in the area. Consistency makes everything standard. Once you know the standard, there is nothing else to tell.

一致性程度取决于周围的文化。例如,在一家大量使用 JEE 的公司中,无需说明您决定使用 EJB 的原因,但您需要解释何时决定不使用它。在另一家品味更好的公司,情况则恰恰相反。

The level of consistency depends on the surrounding culture. For example, in a JEE-heavy company, there would be no need to tell why you decided to use EJB, but you’d need to explain when you decide not to use it. In another company with better taste, it would be the opposite.

如果您作为一个团队决定在域模型中不允许任何方法返回 null,则只需将这一决定记录在一个位置,例如域模型源代码控制系统的根文件夹中。那么各个方法就不用再讲了。

If you decide as a team that no method is allowed to return null within your domain model, then this decision only has to be documented in one place, such as in the root folder of the domain model source control system. Then there’s no need to talk about it anymore on each method.

因此:作为一个团队就在选定的有限区域内应用的具体准则达成一致。将它们简要记录在一处。

Therefore: Agree as a team on concrete guidelines to apply within chosen bounded areas. Document them briefly in one place.

规则必须有例外。并非每个班级都会保持一致。然而,只要异常的数量很少,明确记录异常仍然比记录每个类的所有内容要便宜。

There have to be exceptions to the rule. Not every class will be consistent. However, as long as the number of exceptions is low, it’s still cheaper to document the exceptions explicitly than to document everything on every class.

以下是团队为域模型创建的指南示例:

Here’s an example of the guidelines that a team created for a domain model:

  • 公共签名命名中不使用缩写

  • No abbreviations in naming of public signatures

  • 所有公共接口及其方法中业务可读的名称

  • Business-readable names in all public interfaces and their methods

  • 无 Null:不允许 null 作为返回类型或方法参数

  • Null-free: no null allowed as a return type or as a method parameter

  • 默认情况下所有类都是不可变的

  • All classes immutable by default

  • 所有方法默认无副作用

  • All methods side effect free by default

  • 没有SQL

  • No SQL

  • 根本不导入框架,包括javax

  • No import of frameworks at all, including javax

  • 无需导入基础设施(如中间件)

  • No import of infrastructure (such as middleware)

强制执行指南提供了一种即使没有人阅读也能有效记录指南的方法。

Enforced guidelines provide a way to document the guidelines in a way that is effective even if nobody reads them.

示例:零文档游戏

Example: The Zero Documentation Game

我听说有一个团队决定禁止记录文档。他们自豪地做到了零文档。它并不像乍看起来那么疯狂:零文档是一种强制更好的命名和更好的实践的方法,无需额外的散文即可共享知识。

I’ve heard of a team that decided to forbid documentation. They’re proudly doing zero documentation. It’s not as insane as it might at first seem: Zero documentation is an approach that forces better naming and better practices in general to share knowledge without additional prose.

当您了解到大多数情况下以散文或图表形式编写的文档并不能很好地替代在工作产品本身中更好地表达知识时,将其最小化是有意义的。因为争取零文档听起来很激进而且有点疯狂,所以它很刺激并且变成了一个游戏。这使得它更有可能留在团队成员的脑海中,从而有望推动他们的行为变得更好。

When you understand that most of the time written documentation in the form of prose or diagrams is a poor substitute for expressing the knowledge better within the work product itself in the first place, it makes sense to minimize it. And because striving for zero documentation sounds radical and a bit insane, it’s stimulating and becomes a game. This makes it more likely to stick in team members’ minds, driving their behavior for the better, hopefully.

我自己没有尝试过,但我的同事告诉我,零文档通常会在实践中推动良性行为。

I haven’t tried it myself, but my colleague told me that zero documentation usually drives virtuous behavior in practice.

因为我们对“文档”一词的定义不尽相同,所以零文档游戏必须阐明其规则。前面提到的团队拒绝对代码和方法、所有形式的书面散文、外部文档和传统 Office 文档进行评论。它愉快地拥抱测试和 Gherkin 场景(Cucumber/SpecFlow),喜欢简单的代码,并喜欢将集体工作作为共享知识的主要手段。团队对这一切感到高兴。

Because we don’t all share the same definition of the word documentation, a game of zero documentation must clarify its rules. The previously mentioned team refuses comments in the code and on methods, all forms of written prose, external documents, and traditional Office documents. It happily embraces tests and Gherkin scenarios (Cucumber/SpecFlow), favors simple code, and enjoys working collectively as a primary means of sharing knowledge. The team is happy with all this.

我认为用注释来增强代码、保留一个简单的自述文件以及生成实时文档仍然符合游戏规则,但您可以决定将光标放在哪里!

I think augmenting the code with annotations, keeping a simple README file, and generating living documents would still fit within the rules of the game, but you decide where to put the cursor!

持续培训

Continuous Training

随着常识变得越来越普遍,您需要记录的东西就越少。因此,投资持续培训是减少文档需求的一种方法。

As general knowledge becomes more widespread, the less you need to document. Investing in continuous training is therefore a way to reduce the need for documentation.

学习标准技能还可以更轻松地使用更多现成的知识而不是原始解决方案。这有利于解决方案的质量,并且减少了对特定文档的需求。

Learning standard skills also makes it easier to use more ready-made knowledge instead of original solutions. This is good for the quality of the solution, and it alleviates the need for specific documentation.

技能和共享文化的一致性也有助于加快决策速度。这并不是要消除团队中的所有多样性,因为多样性是一个重要组成部分。尽管如此,我们并不需要每个细节都具有多样性,而且我们可以在不损失太多的情况下使很多东西变得更加一致。

More consistency of skills and shared culture also helps speed up decision making. It’s not about removing all diversity in the team, since diversity is an essential ingredient. Still, we don’t need all diversity in every detail, and there’s a lot that we can make more consistent without losing much.

持续培训的投资可能涉及以下内容:

Investing in continuous training may involve the following:

  • CodeKata 上的编程道场(例如,每周五午餐时间)

  • Coding dojos on CodeKata (for example, at lunchtime every Friday)

  • 白天进行短期训练

  • Short training sessions during the day

  • 交互式迷你培训(例如,每周两次,午餐后半小时)

  • Interactive mini-trainings (for example, half an hour twice a week right after lunch)

  • 刻意练习的时间(例如,20%的时间用于副项目)32

  • Time for deliberate practice (for example, a 20%-time policy devoted to side projects)32

32. https://www.inc.com/adam-robinson/google-employees-dedicate-20-percent-of-their-time-to-side-projects-heres-how-it-works.html

32.https://www.inc.com/adam-robinson/google-employees-dedicate-20-percent-of-their-time-to-side-projects-heres-how-it-works.html

概括

Summary

最好的文档就是看起来不像文档。在分享知识方面,互动对话和集体工作排名非常高。此外,在咖啡机旁的偶然和自发的相遇也是一个重要的补充。

The best documentation is doesn’t look like documentation. Interactive conversations and working collectively rank very high when it comes to sharing knowledge. In addition, the serendipity and spontaneity of meeting at the coffee machine is an essential complement.

另一方面,通过纪律或通过更好的工具使流程自动化更具声明性,也使其成为流程知识的适当权威。拥有当你做错事时尖叫的工具是另一种形式的文档,它是最有效的一种,因为它在正确的时间甚至为那些不知道的人带来了正确的知识。

On a different note, making process automation more declarative, by discipline or through better tools, also makes it the proper authority of knowledge for a process. Having tools that scream when you do something wrong is yet another form of documentation, and it is one of the most efficient, since it brings the right knowledge at the right time even to people who weren’t aware of it.

在本章列出的所有案例中,关键点是我们需要提高开发团队已经执行的所有活动的文档价值,以减少专门完成文档任务的工作。仅仅因为某些东西看起来不像典型的文档并不意味着它不是知识共享和保存的有效形式。开发人员及其经理越了解这一点,他们的集体效率就会越高。

In all the cases listed in this chapter, the key point is that we need to turn up the documentation value of all the activities already carried out by the development teams in order to reduce the work that is specifically done on documentation tasks. Just because something doesn’t look like typical documentation doesn’t mean it’s not a valid form of knowledge sharing and preservation. The more developers and their managers understand this, the more efficient they will collectively be.

第11章

Chapter 11

超越文档:生活设计

Beyond Documentation: Living Design

到目前为止,本书重点讨论如何记录和传递有关软件项目中已完成工作的知识。然而,当您开始明确地关注这些知识时,就会产生额外的好处:您开始看到设计的改进。当您致力于创建活文档时,您通常还会看到设计改进,这种好处很快就会比文档方面重要得多。您最初的目标是能够跟踪设计更改的实时文档,逐渐演变成实时文档,开始建议对设计进行更多更改!本章探讨了许多可以帮助您最大限度地利用这种奖励效果的模式。

This book has so far focused on how to record and transfer knowledge about what has been done in a software project. However, when you start paying attention to this knowledge explicitly, an additional benefit kicks in: You start to see improvements in your design. As you’re working to create living documentation, you often also see design improvement, a benefit that can quickly be far more important than just the documentation aspect. Your initial goal of living documentation that can follow design changes morphs into living documentation that begins to suggest even more changes to the design! This chapter explores a number of patterns that can help you exploit this bonus effect to the max.

作为另一个额外效果,使软件系统的内部对所有利益相关者更加可见,将使其更有可能得到良好的设计。

As one more bonus effect, making the inside of the software system more visible to all stakeholders will make it increasingly likely that it will be well designed.

聆听文档

Listening to the Documentation

您已经了解了一些有关活文档的知识,并且想尝试一下。如果您尝试创建一个活动图表,但发现很难从当前源代码生成一个图表,那么这是一个信号。如果您尝试生成一个活生生的术语表,但发现这几乎不可能实现,这也是一个信号。如图 11.1所示,您应该聆听信号。

So you’ve learned a bit about living documentation, and you want to try it. If you try to create a living diagram but find it hard to generate one from the current source code, this is a signal. If you try generate a living glossary but find this almost impossible to achieve, this is also a signal. As illustrated in Figure 11.1, you should listen to the signals.

图中显示一个卡通人物拿着麦克风,指着一张纸条。

图 11.1 听听你的文档!

Figure 11.1 Listen to your documentation!

Nat Pryce 和 Steve Freeman 写道:“当代码难以测试时,最可能的原因是我们的设计需要改进。” 1同样,如果您发现很难从代码生成实时文档,则表明您的代码存在设计问题。

Nat Pryce and Steve Freeman wrote, “when code is difficult to test, the most likely cause is that our design needs improving.”1 Similarly, if you find it hard to generate living documents from your code, it’s a signal that your code has design issues.

1.弗里曼、史蒂夫和纳特·普莱斯。以测试为指导,不断发展面向对象的软件。波士顿:培生教育公司,2010 年。

1.Freeman, Steve, and Nat Pryce. Growing Object-Oriented Software, Guided by Tests. Boston: Pearson Education, Inc. 2010.

领域语言发生了什么?

What Happened to the Language of the Domain?

如果您热衷于 DDD 并且发现很难生成业务领域语言的动态术语表,那么可能是因为这种语言在代码中表达得不够清楚。可能会发生以下任一情况:

If you’re into DDD and you find it hard to generate a living glossary of the business domain language, then it’s probably because this language is not expressed clearly enough in the code. Any of the following might be happening:

  • 该语言可能会用其他单词来表达,例如技术单词、同义词或(最糟糕的)遗留数据库名称。

  • The language might be expressed in other words, such as technical words, synonyms, or (worst) legacy database names.

  • 该语言可能以一种无法恢复的方式与技术问题混合在一起;例如,业务逻辑可能与数据持久性逻辑或表示问题混合在一起。

  • The language might be mixed with technical concerns in a way that is impossible to recover; for example, business logic may be mixed with data persistence logic or presentation concerns.

  • 语言可能完全丢失,并且代码可能在不引用相应业务语言的情况下执行业务操作。

  • The language might be completely lost, and code may be doing business stuff without any reference to the corresponding business language.

无论出现什么问题,如果您发现编写活文档很困难,您应该将其视为您正在错误地进行 DDD(以及一般领域建模)的信号。设计应尽可能地与业务领域及其语言保持一致。

Whatever the issue, if you’re finding it difficult to do living documentation, you should take it as a signal that you’re doing DDD—and domain modeling in general— incorrectly. The design should be aligned as much as possible with the business domain and its language, word by word.

因此,您应该抓住机会重新设计代码,以便更好地表达领域语言,而不是尝试制作一个复杂的工具来生成动态术语表。当然,由您决定这样做是否合理以及何时以及如何做。

So instead of trying to make a complicated tool to generate a living glossary, you should take the opportunity to redesign the code so that it better expresses the domain language. Of course, it’s up to you to decide whether it’s reasonable to do this and when and how to do it.

通过巧合设计进行编程

Programming by Coincidence Design

我们不知道自己在做什么,也不知道自己做了什么。

We don’t know what we’re doing, and we don’t know what we’ve done.

——弗雷德·布鲁克斯2

—Fred Brooks2

2. Fred Brooks(1999 年图灵奖)的这句话是他的工作组关于科学在设计中的作用的总结,如“软件语言工程:第六届国际会议”第 15 页所述,SLE 2013,印第安纳波利斯,印第安纳州,美国,2013 年 10 月 26 日至 28 日。

2.This quote from Fred Brooks (1999 Turing Award) was the summary of his working group on the role of science in design, as reported on page 15 in “Software Language Engineering: 6th International Conference,” SLE 2013, Indianapolis, IN, USA, October 26-28, 2013.

如果没有选择,你就不是在做设计。

If there is no choice to be made, you’re not doing design.

——卡洛·佩西奥3

—Carlo Pescio3

3.Carlo Pescio,“设计、结构和决策”, http://www.carlopescio.com/2010/11/design-struct-and-decisions.html

3.Carlo Pescio, “Design, Structure, and Decisions,” http://www.carlopescio.com/2010/11/design-structure-and-decisions.html

要生成设计图,首先您必须知道您希望该图解释什么特定的设计决策。但你能说出你的设计是什么样的吗?尝试生成生动的图表时最常见的困难很简单,就是您通常不太清楚您的设计是什么样的,或者为什么会这样。这表明您可能是巧合地进行编程。4您可能知道如何使您的设计发挥作用,但您并不真正知道原因,也没有真正考虑过替代方案。这样的设计是任意的而不是刻意的。

To generate a design diagram, first you have to know what particular design decision you expect the diagram to explain. But can you tell what your design is like? The most common difficulty when trying to generate living diagrams is simply that you often don’t know clearly enough what your design is like or why it’s that way. This suggests that you may be programming by coincidence.4 You might know how to make your design work, but you don’t really know why, and you haven’t really considered alternatives. Such a design is arbitrary rather than deliberate.

4.安德鲁·亨特和大卫·托马斯。务实的程序员:从熟练工到大师。波士顿:艾迪生韦斯利,2000。

4.Hunt, Andrew and David Thomas. The Pragmatic Programmer: From Journeyman to Master. Boston: Addison-Wesley, 2000.

笔记

Note

我喜欢卡洛·佩西奥的散文。我实际上不太喜欢他的写作风格,但我喜欢他写下他对软件开发的困难和深刻问题的思考的方式。他有一些疯狂的想法和一些延伸的隐喻,但也有很多见解激发了我对我们领域未来突破的想象力。要了解我的意思,请访问http://www.carlopescio.com

I love Carlo Pescio’s essays. I don’t actually like his writing style much, but do I like the way he writes about his mind musing on hard and deep matters of software development. He’s got some crazy ideas and some stretched metaphors but a lot of insights to spark my imagination about future breakthroughs in our field. To see what I mean, visit http://www.carlopescio.com.

构建软件涉及持续的决策。重大决策通常会受到广泛关注,包括专门会议和书面文件,而被认为不太重要的决策往往会被忽视。问题在于,许多被忽视的决定最终都是武断的,而不是经过深思熟虑的,而且累积效应(甚至复合效应)可能会使源代码难以使用。

Building software involves continuous decision making. Big decisions usually get a lot of attention, including dedicated meetings and written documents, and decisions deemed less important tend to be neglected. The problem is that many of the neglected decisions end up being arbitrary rather than well-thought-out, and the accumulated effect (even a compounding effect) may be to make the source code hostile to work with.

“为什么这个函数返回 null 而不是空列表?” “为什么有些函数返回 null 而其他函数不一致地返回空列表?” “为什么大部分 DAO 都在这个包中,但不是全部?” “为什么我们在五个不同的类中有相同的方法签名,但没有一个通用的接口来统一它们?” 这种被忽视的决定有时接近更好的解决方案,但由于未能正确思考手头的问题而错过了它们。所有这些例子都代表着失去了更好设计的机会。

“Why does this function return null instead of an empty list?” “Why do some functions return null and others return an empty list inconsistently?” “Why are most of the DAO, but not all, in this package?” “Why do we have the same method signature in five different classes but without a common interface to unify them?” Such neglected decisions sometimes get close to better solutions but miss them due to failure to properly thinking about the matters at hand. All these examples represent lost opportunities for better design.

提示

Tip

每当您在代码或代码设计中发现意外情况时,请考虑思考以下问题:“如何才能回到文献中的标准情况?”

Whenever you find out something unexpected in code or in code design, consider thinking about the question, “What would it take to come back to the standard situations in the literature?”

我鼓励深思熟虑。记录决策是鼓励更深入思考的一种方式,因为试图解释决策往往会暴露其弱点。

I encourage deliberate thinking. Documenting decisions as they are made is one way to encourage deeper thinking because trying to explain a decision often reveals its weaknesses.

笔记

Note

如果你不能简单地解释某件事,说明你还没有充分理解它。

If you can’t explain something simply, you don’t understand it well enough.

有时,当与客户现场的团队合作观察决策的制定而没有人清楚推理时,这会令人沮丧。“现在就让它发挥作用”似乎是座右铭。在一个例子中,我记录了这样一种情况:

Sometimes it’s frustrating when working with a team at a customer site to observe decisions being made without anyone being clear on the reasoning. “Just make it work right now” seems to be the motto. In one instance I took notes about one such situation:

我们已经讨论了遗留应用程序和新的基于事件源的应用程序之间的消息语义一个小时。是事件还是命令?和往常一样,讨论并没有得出明确的结论,但不明确的选择却起作用了。如果我们决定清楚地记录所有集成交互的语义,我们就必须做出决定,并将其变成标签或书面可见的东西。然后我们就必须遵守它,或者当它不再相关时明确质疑它。

We’ve been discussing for one hour the semantics of the messages between a legacy app and a new event-sourcing-based app. Is it event or command? As usual, the discussion doesn’t lead to a clear conclusion, and yet the unclear choice works. Had we decided to document the semantics of all integration interactions clearly, we would have had to decide, and to turn it into a tag or something written and visible. Then we would have to conform to it, or to question it explicitly when it’s no longer relevant.

相反,我们将生活在持续的混乱之中。每个贡献者将按照他或她的意愿进行解释。它会咬我们。

Instead, we’re going to live with the continuous confusion. Each contributor will interpret as he or she wishes. And it will bite us.

一年后,我可以看到团队已经成熟,现在这样的讨论会收敛到合理的推理。

A year later, I can see that the team has matured, and now such a discussion would converge to sound reasoning.

深思熟虑的决策

Deliberate Decision Making

通往更好的设计和更好的文档的道路始于有意识地做出更多决策。记录随机决策非常困难。这就像试图描述噪音:同时存在太多低级细节,而在更高级别上几乎没有什么可说的。相比之下,当决策是经过深思熟虑时,它们是明确且有意识地做出的,而记录只是将它们变成文字的简单问题。

The path to better design and better documentation starts by making more decisions deliberately. It is very difficult to document random decisions. It is like attempting to describe noise: There are at the same time too many low-level details and almost nothing to tell at a higher level. In contrast, when decisions are deliberate, they are clearly and consciously made, and documentation is a simple matter of putting them into words.

如果一个决策相当标准,那么它就是已经在书中以标准名称(例如模式)讨论过的现成知识。在这种情况下,记录只需要在代码中标记标准名称,以及一些简短的原因、动机、背景和导致该决定的主要力量。

If a decision is pretty standard, it’s ready-made knowledge that has been already discussed in a book under a standard name, such as a pattern. Documenting in such a case involves just making a mark in the code that refers to the standard name, along with some brief reasons, motivation, context, and main forces that led to the decision.

提示

Tip

如果一个决定是经过深思熟虑的,那么它已经有一半记录在案了。

If a decision is deliberate, it’s already half documented.

深思熟虑的工作方式是敏捷圈子中反复出现的一个重要主题。软件工艺鼓励刻意练习来改进工艺。我们花时间练习型和编码道场,以实现提高技术水平的目标。在 BDD 社区中,Dan North 解释说,项目应该被视为学习举措,他将这种心态称为“刻意发现”5他声称我们应该尽一切努力尽快、尽早地学习。深思熟虑是指付出额外的努力,有意识地把工作做得更好。

Being deliberate in the way we do our work is a big recurring theme in agile circles. Software craftsmanship encourages deliberate practice to improve the craft. We dedicate time to practice katas and coding dojos to achieve that goal of getting better at our craft. In the BDD community, Dan North explains that projects should be seen as learning initiatives, a mindset he calls deliberate discovery.5 He claims that we should do whatever it takes to learn as quickly as possible as early as possible. Being deliberate is about expending extra effort to do better work in a conscious way.

5. https://dannorth.net/2010/08/30/introducing-deliberate-discovery/

5.https://dannorth.net/2010/08/30/introducing-deliberate-discovery/

深思熟虑的设计涉及清楚地思考每个设计决策。目标是什么?有什么选择?我们确实知道什么,我们怀疑什么?对于这种情况,文献是怎么说的?

Deliberate design involves thinking clearly about each design decision. What is the goal? What are the options? What do we know for sure, and what do we suspect? What does the literature say on this kind of situation?

此外,设计越好,需要记录的内容就越少。更好的设计更简单,“更简单”实际上意味着更少但更强大的决策,可以解决更多问题:

In addition, the better the design, the less there is to document. Better design is simpler, and “simpler” actually means fewer but more powerful decisions that solve more of the problem:

  • 对称性:相同的代码或接口处理所有对称情况。

  • Symmetries: The same code or interface takes care of all symmetric cases.

  • 更高层次的概念:相同的代码同时处理许多特殊情况。

  • Higher-level concepts: The same code deals with many special cases at once.

  • 一致性:某些决定在任何地方都会重复,无一例外。

  • Consistency: Some decision is repeated everywhere without exception.

  • 可替换性和封装:边界内的局部决策并不重要,因为它们可以在以后重新考虑或重做,即使有关它们的知识丢失了。

  • Replaceability and encapsulation: Local decisions within a boundary do not matter, as they can be reconsidered or redone later, even if knowledge about them is lost.

记录软件所需的特定知识的数量是设计成熟度的指标。可以用 10 句话描述的软件比需要 100 句话描述的软件具有更好的设计。

The quantity of specific knowledge needed to document a piece of software is  an  indicator of the maturity of the design. Software that can be described in 10 sentences has a better design than software that needs 100 sentences of description.

工程是一种刻意的实践

Engineering Is a Deliberate Practice

在法国工程学院和其他高等院校,从机械工程到电子工程甚至工业设计,学生最重要的是证明他们所做的所有决定都是经过证实的。武断的决定是不可接受的。

In French engineering schools and other grandes ecoles, from mechanical engineering to electronic engineering or even industrial design, it’s primarily important for students to demonstrate that all decisions they have made are substantiated. Arbitrary decisions are just not acceptable.

在期末考试期间,评估的最重要方面涉及精确地构建工作,然后在设计解决方案的每个步骤中,每个决定都必须根据足够的替代方案进行论证,并根据明确的标准进行选择:预算、权重、可行性或其他限制。

During final exams, the most important aspects of the evaluation involve framing the work precisely, and then at each step of designing the solution, each decision has to be justified against enough alternatives and chosen according to explicit criteria: budget, weight, feasibility, or other constraints.

在软件开发中,我们很少对每个细节都如此深思熟虑,但我们应该如此。无论每个决定是否以书面形式记录下来,做出更有意识的决定通常都会改善决策。

In software development, we are seldom so deliberate in every detail, but we should be. Whether every decision is recorded in writing or not, making more conscious decisions often improves the decisions.

如果您知道自己在做什么、文献中的名称以及做出特定决定的原因,那么完整的文档所需要做的就是在代码中将这些信息添加到一行中:指向文献的链接和一些文字来解释其基本原理。一旦你有了正确的想法,写作就会水到渠成。

If you know what you’re doing, what it’s called in the literature, and why you’ve made a particular decision, all it takes for complete documentation is to add that information in the code in one line: a link to the literature and some text to explain the rationale. Once you’ve got the thinking right, the writing takes care of itself.

当然,你必须意识到,思考需要时间。它看起来很慢,可能会与松弛相混淆,并且在许多公司中,人们常常会想:“我们没有时间这样做!” 然而,思考的替代方案只会给人一种速度快的错觉,而牺牲了准确性。正如怀亚特·厄普 (Wyatt Earp) 所说,“速度固然很好,但准确性才是一切。” 准确性需要严谨的思维。用多个大脑思考,例如结对编程或群体编程,也可以提高准确性并帮助您创建更深思熟虑的设计。有了更多的大脑,人们就更有可能从文献中了解任何情况的标准解决方案。

You have to realize, of course, that thinking takes time. It looks slow and may be confused with slack, and in many companies people often think, “We don’t have time for that!” However, alternatives to thinking only give the illusion of speed, at the expense of accuracy. As Wyatt Earp said, “Fast is fine, but accuracy is everything.” Accuracy requires rigorous thinking. Thinking with more than one brain, as in pair programming or mob programming, also improves accuracy and helps you create more deliberate designs. With more brains, it’s more likely that someone knows the standard solutions from the literature for any situation.

您可能听过这样一句话:“除非你能向别人解释某件事,否则你并没有真正理解它。” 为了记录的目的而必须澄清你的想法是有好处的,因为你必须澄清你的想法。必须以持久的形式证明决策的合理性是更严格思考的另一个动机。

You have probably heard this saying: “You don’t really understand something until you can explain it to someone else.” Having to clarify your thoughts for documentation purposes is virtuous because, well, you have to clarify your thoughts. Having to justify decisions in a persistent form is another incentive to think with more rigor.

笔记

Note

在进行 TDD 时,深思熟虑的设计尤其有效。TDD 是一种非常刻意的规则实践。从可以运行的简单代码开始,设计通过连续的重构而产生,但推动重构的是开发人员,他们必须在应用每次重构之前进行思考。“我们真的需要让事情变得更复杂吗?” “现在值得添加一个界面吗?” “我们是否应该引入一种模式来替换这两个 IF 语句?” 这都是关于权衡的,这需要清晰的思维。

Deliberate design works particularly well when doing TDD. TDD is a very deliberate practice with rules. Starting with naive code that just works, the design emerges from successive refactorings, but it’s the developers who are driving the refactorings, and they have to think before applying each refactoring. “Do we really need to make that more complex?” “Is it worth adding an interface now?” “Shall we introduce a pattern to replace these two IF statements?” It’s all about trade-offs, which requires clear thinking.

活文档鼓励人们关注良性实践——尤其是设计。活文档使糟糕的设计清晰可见。最大的回报之一是您可以改进设计,因此您的设计文档几乎是免费的。

Living documentation encourages attention to virtuous practices—design in particular. Living documentation makes bad design clearly visible. One of the great rewards is that you can improve the design, and your design documentation comes almost for free as a result.

作者的自白

A Confession from the Author

刻意的设计是我写这本书的别有用心。人们对设计没有足够的重视,对此我感到非常遗憾。活文档是特洛伊木马,或者是让更多人沉迷于更好设计的门户。

Deliberate design is my ulterior motive in writing this book. People don’t pay enough attention to design, and I’m very sorry about that. Living documentation is a Trojan horse, or a gateway to get more people addicted to better design.

“深思熟虑的决定”并不意味着“预先决定”

“Deliberate Decision” Does Not Mean “Upfront Decision”

随着新兴设计的出现,通过听取工作代码及其缺陷,自然会做出决定。例如,注意到重复可能会触发重构以获得更好的东西。当你达到这样的地步时,你必须做出一个有意识的、深思熟虑的决定:你想要重构的“更好”是什么?深思熟虑意味着你了解麻烦,可以想象你正在寻找的好处,并且找到了不止一种改进方法。决定意味着从所有可能的其他方法中选择一种方法。这是一个深思熟虑的决定。

With emerging design, the natural decision emerges by listening to the working code and its flaws. For example, noticing duplication may trigger a refactoring to something better. When you reach such a point, you have to make a conscious, deliberate decision: What is the “better” you want to refactor to? Deliberate means that you understand the troubles, can imagine the benefits you are looking for, and have found more than one way to improve. Deciding means choosing one way out of all the possible others. This is a deliberate decision.

文档是代码审查的一种形式

Documentation Is a Form of Code Review

文档使产品和开发过程更加透明。因此,文档也是一种有用的反馈工具,可以帮助您在应用程序的整个生命周期中进行调整和纠正。没有理由的决定无处可藏。借助生动的图表和生动文档的其他想法,被忽视的设计区域变得非常明显,使人们更难以忽视它们。这增加了对代码质量各个方面更加关注的压力。

Documentation makes a product and the development process more transparent. As such, documentation is also a useful feedback tool that helps you adjust and correct over the complete lifecycle of an application. Decisions with no rationale have nowhere to hide. With living diagrams and the other ideas of living documentation, the neglected design areas become quite visible, making it harder to ignore them. This increases the pressure to put more care into every aspect of code quality.

从源代码生成的实时文档(尤其是图表)也可以作为调试工具很好地检测错误,例如依赖项中的意外循环或过度耦合(在图表上显示为太多箭头)。您可能一直期待一些设计结构,但是当尝试将其呈现为图表时,您可能不得不承认代码没有表现出太多结构。您可能一直期望代码能够告诉业务领域,但是当尝试将其放入术语表时,可能会出现业务在处理过程中被破坏的情况,并且没有简单的方法可以将其取出。

Living documentation generated from source code, especially diagrams, also works great as a debugging tool to detect mistakes such as unexpected cycles in the dependencies or excessive coupling, shown as too many arrows on the diagram. You might have been expecting some design structure, but when trying to render it as a diagram, you may have to admit that the code does not exhibit much structure. You might have been expecting the code to tell the business domain, but when trying to make it into a glossary, it may appear that the business is mangled in the middle of the processing, and there is no easy way to get it out.

将构建代码之前可能完成的自上而下的文档与从源代码生成的实际自下而上的文档进行比较是很有趣的。这些差异可以帮助您发现不一致之处,或者更好的是,再次认识到在实际开发之前很难推测代码会是什么样子。

It is interesting to compare the top-down documentation you might have done before building the code with the actual bottom-up documentation generated from the sources. The differences can help you spot inconsistencies or, even better, to realize once again that it is difficult to speculate about what the code will be like before it is actually developed.

事实上,即使在制作生动的图表之前,仅仅尝试在纸上手工记录就可以揭示设计问题。我的一位客户的首席开发人员 Maxime Sanglan 在阅读本书的早期版本时做出了反应:“当我开始让团队在遗留系统上围绕 Simon Brown 的 C4 模型进行草图研讨会时,就完全发生了这种情况。”

Indeed, even before making living diagrams, just trying to document by hand on paper can reveal design issues. Maxime Sanglan, a lead developer from one of my clients, reacted when reading an early version of this book: “That’s totally what happened when I started to have the team do sketching workshops around Simon Brown’s C4 model on the legacy system.”

可耻的文档

Shameful Documentation

仅仅因为它被记录下来,并不意味着它就不那么愚蠢了。

Just because it is documented, it doesn’t make it less stupid.

—推特上的@dalijap

—@dalijap on Twitter

当文档是最新且准确的时,通常被认为是一件好事。然而,在很多情况下,情况恰恰相反:文档本身的存在就表明了问题的存在。臭名昭著的故障排除指南是此类中最好的例子。有人决定花时间记录已知的问题、使用陷阱和其他异常行为,这一努力表明这些问题足够重要,值得记录。然而,这也意味着这些问题没有得到解决,甚至可能没有人计划解决它们。

When documentation is up-to-date and accurate, it is often considered a good thing. However, there are a number of cases where quite the opposite occurs: The existence of the documentation itself demonstrates the presence of a problem. The infamous troubleshooting guide is the best example in this category. Someone decided to take the time to document the known troubles, usage traps, and other anomalies of behavior, and this effort demonstrates that the issues are important enough to be worth documenting. However, it also means that these issues are not fixed, and it is possible that no one even plans to fix them.

这样的文档我称之为可耻的文档;这是你应该感到羞耻的文档。该文档就其唯一存在而言,应被视为对需要修复的问题的承认。创建此文档所花费的时间应该用于解决问题。

Such documentation is what I call shameful documentation; it’s documentation you should be ashamed of. This documentation, by its sole existence, should be seen as a confession of something to be fixed. The time spent creating this documentation should have been allocated to fixing the troubles instead.

因此:认识到文档不足以替代实际解决问题的情况。只要有可能,就决定不添加更多文档,而是分配时间来解决问题。

Therefore: Recognize the situations when documentation is a poor substitute for actually fixing a problem. Whenever possible, decide against adding more documentation and instead allocate time to fixing the problem.

当然,团队添加文档而不是解决问题的原因有很多:

Of course, there are many reasons teams might add documentation instead of fixing the issues:

  • 预算:可能会分配用于文档的资金,但不会再分配用于编写代码的资金。

  • Budget: There might be money allocated for documentation but no more money for working on the code.

  • 懒惰:添加一些有关故障排除的快速文档似乎比实际解决根本问题更容易。

  • Laziness: It may seem easier to add some quick documentation on troubleshooting rather than actually tackle the root issue.

  • 缺乏时间:记录问题可能比解决问题更快。

  • Lack of time: Documenting the issue might be faster than fixing it.

  • 成本:解决某些问题可能确实很困难。例如,某些问题需要向数十个客户端发布应用程序的新版本,这将是非常昂贵的。

  • Cost: It may be genuinely difficult to address some issues. For example, some issues would require releasing a new version of the application to dozens of clients, which would be prohibitively expensive.

  • 缺乏知识:有时团队知道问题,但缺乏解决问题所需的知识和技能。

  • Missing knowledge: Sometimes the team knows about issues but is missing knowledge and skills required to fix the issues.

如果现在没有时间修复问题,那么记录问题的正确位置是缺陷跟踪器。然而,在可耻的文档思维中,缺陷跟踪器本身也体现了一个更深层次的问题:缺陷不应该累积,而应该尽早预防或立即修复。而能够长期存在而不被修复的缺陷真的是缺陷吗?

If there is no time available to fix an issue now, then the right place to document the issue is the defect tracker. However, in the mindset of shameful documentation, a defect tracker is also in itself a demonstration of a deeper issue: Defects should not accumulate but should be prevented earlier or fixed immediately as much as possible. And are defects that can remain for a long time without being fixed really defects?

如果某个功能的实现非常糟糕,以至于需要包含许多页警告和解决方法说明的手册,或者需要支持团队的大量帮助,那么您可能会考虑将其删除,直到它正确实现为止;无论如何,几乎没有人能够使用它,或者使用它的成本太高,以至于不值得。

If a feature is implemented so badly that it requires a manual with many pages of warnings and workaround instructions or a lot of assistance from the support team, you might consider removing it until it is implemented correctly; chances are that almost nobody manages to use it anyway or that using it is so expensive that it’s not worth it.

示例:可耻的文档

Example: Shameful Documentation

在过去的一次客户现场任务中,我发现了一份 16 页的文档,介绍如何运行和测试应用程序。本指南适用于所有用户,包括最终用户。我将这个应用程序称为“Icare”,以保护无辜者。这不是一个新项目;而是一个项目。公司里的几十个人每天都会使用它好几次。该文档充满了以红色气泡突出显示的屏幕截图,以显示如何继续,因为这些步骤并不不直观。然而,这 16 页中的大部分都描述了去哪里“注意”:“注意……[这可能无法正常工作]。请注意……[这里有一个错误]。” “注意,Icare是从另一个目录启动的!” “请务必小心,不要在任何时候启动这些任务,因为它会杀死相应环境中的所有内容!” 它也可能是说“注意;我们不专业。”

In a past mission at a customer site, I discovered a 16-page document on how to run and test an application. This guide was for the all users, including end users. I’ll call this application Icare to protect the innocents. This was not a new project; it was used several times every day by dozens of people in the company. The document was full of screenshots highlighted in red color bubbles to show how to proceed because the steps were not unintuitive. However, most of the 16 pages described where to “pay attention”: “Pay attention…[this may not work properly]. Please note that… [there is a bug here].” “Pay attention, Icare is launched from another directory!” “Take really good care to not launch these tasks any time because it will kill everything on the corresponding environment!” It might as well have said “Pay attention; we’re not professional.”

所写的内容有一半是关于等待咬住用户的陷阱。“注意触发器的名称;有时,它的命名不正确,因此请检查触发器。” 请记住,这是面向最终用户的文档。它甚至变得更好:“在 XML 中导出后,您应该进行重新导入测试,以确保它运行良好。” 您可以看到开发人员有时间编写此文档而不是修复代码。

Half of everything written was about traps waiting to bite users. “Pay attention to the name of the trigger; sometimes, it’s not correctly named, so check in the trigger.” Remember that this was a document for end users. And it got even better: “After an export in XML, you should do a test of re-import to be sure that it works well.” You can see that a developer had the time to write this document instead of fixing the code.

该文档还说,“注意:分区 Icare_env1 和 Icare_env2 在 UAT 和 PROD 之间是相反的!!!” 啊,原来大家都知道这件事,而且这么多年了,却没有人计划去解决吗?或者这个过程是否如此繁重以至于您必须首先找到赞助商来支付修复费用?

The document also said, “Pay attention: Partitions Icare_env1 and Icare_env2 are inversed between UAT and PROD!!!” Ah, so everyone knew about this, and it had been like that for years, but it wasn’t anyone’s plan to fix it? Or was the process so heavyweight that you’d first have had to find a sponsor to pay for the fix?

故障排除指南

The Troubleshooting Guide

最后,文档末尾是臭名昭著的“已知问题”部分,如下所示:

Finally, at the end of the documentation was the infamous “known problems” section, shown here:

1 1 已知问题
2
3 1.1 Icare作业未启动
4
5 这种事经常发生。首先,尝试直接从 Icare 启动它(所以
6 从正确的目录手动启动应用程序
7 [c:/icare/uat1/bin 对于 UAT,c:/icare/prod/bin 对于 PROD])。
8
9
10 如果您无法手动启动它,这是因为
作业配置不正确
11(参数日期或计算日期缺失或不正确,
ETC。)。
12 如果运行正常,则在启动Icare时出现问题
命令行,所以你需要检查日志(找到哪里
13 它记录,检查 icarius_mngt.exe.log4net)。
14
15
16 以前,第一个也存在问题
执行。
17 号它需要手动连接到
具有良好登录(IcariusId)的环境。
18
19 当建立第一个连接时,批处理模式为
正确工作。
20
1  1 Known problems
2
3  1.1 Icare Job does not start
4
5  It often happens. First, try to launch it directly from Icare (so
6 launch the application manually from the correct directory
7 [c:/icare/uat1/bin for UAT, c:/icare/prod/bin for PROD]).
8
9
10 If you are not able to launch it manually, it's because
configuration of the job is not correct
11  (missing or incorrect parameter date or calculation date,
etc.).
12 If it runs well, there is a problem when launching Icare in
command line, so you need to check the log (to find where
13 it logs, check the icarius_mngt.exe.log4net).
14
15
16 In the past, there was also a problem for the first
execution.
17 It requires to have made a manual connection to the
environment with the good login (IcariusId).
18
19 When a first connection was established, the batch mode was
correctly working.
20

请注意应用程序 Icarius 和 Icare 的命名不一致。

Notice the inconsistent naming of the application as Icarius and Icare.

可耻的文档并不总是意味着错误;相反,它可能会建议更多操作友好性的机会,如本示例所示:

Shameful documentation does not always mean bugs; it may instead suggest opportunities for more Ops-friendliness, as shown in this example:

1“你必须检查缓存是否已启动,否则它们会命中
2 DB与降级性能结果"
3
4 [...]
5
6“非常重要:
7 由于我们无法保证同步
8 在工作期间,两个环境都无法启动
9种不同类型的工作”。
1 "you have to check the caches are up otherwise they will hit
2 the DB and degrade performance results"
3
4 [...]
5
6 "Very important :
7 As we are not able to guarantee the synchronization of the
8 two environments for the duration of jobs, we cannot launch
9 different type of jobs".

当您仔细聆听文档时,您会发现它是建议的来源。是否有一种自动监视缓存的方法,或者更好的一种机制来确保它们始终在操作之前预加载?添加一个安全机制怎么样,这样如果你犯了错误,你就会收到警告,并且可以避免这个问题?

When you listen carefully to the documentation, you see that it is a source of suggestions. What about having a way to automatically monitor the caches or, even better, a mechanism to ensure that they are always preloaded before operations? What about adding a safety mechanism so that if you make the error, you’re warned, and you can avoid the issue?

可耻的代码文档

Shameful Code Documentation

您不必忍受有记录的疼痛。说不。写这样的文档很浪费时间,读它也很浪费时间,甚至不能完全防止任何人陷入陷阱,这会一次又一次地浪费更多的时间。

You don’t have to tolerate documented pain. Say no. Writing such documentation is wasting time, and reading it is wasting time, too, and it will not even prevent anyone from falling into the trap completely, which will waste yet more time—again and again.

Icare 故障排除指南只是可耻文档的一个例子。任何变得太大的文档都会成为可耻的文档。100页的开发者指南暴露了代码质量问题,厚厚的用户手册也不方便用户使用。对于使用起来不直观的应用程序,您可能需要一份大型用户指南,但如果您关心用户,那么解决真正的问题将是更好的投资。

The Icare troubleshooting guide is just one example of shameful documentation. Any document that is getting too big becomes a case of a shameful documentation. A developer guide with 100 pages reveals issues of code quality, and a thick user manual is not user-friendly. You might need a big user guide for an application that is not intuitive to use, but addressing the real issue instead would be a better investment if you care about the users.

同样,对于软件设计,如果需要大量的页面和图表来解释应用程序的架构,那么它很可能很糟糕。(有关架构文档的更多信息,请参阅第 12 章“实时架构文档”。)

Similarly, with software design, if it takes a lot of pages and many diagrams to explain the architecture of an application, then it is most likely poor. (See Chapter 12, “Living Architecture Documentation,” for more on documenting the architecture.)

最后,可耻的文档也适用于代码。每当开发人员感到有必要添加这样的注释时,它应该触发删除注释并立即修复有问题的代码的反应:

Finally, shameful documentation also applies to code. Every time a developer feels the urge to add a comment like this, it should trigger a reaction to remove the comment and immediately fix the questionable code instead:

1 // 注意这个棘手的情况
2 ...
3 // 永远不应该发生
4 ...
5 // FIXME:删除这个黑客​​!
1  // beware this tricky case
2  ...
3  // should never happen
4  ...
5  // FIXME: remove this hack!

记录错误还是避免错误?

Documenting Errors or Avoiding Errors?

代码中的注释并不是表明有人需要改进代码的唯一信号。如果您学会通过使用更好的设计和编码实践来完全避免错误情况,那么专门处理错误处理并且传统上需要特定文档的代码可能会变得多余。

Comments in code are not the only signals which suggest that someone needs to improve the code. Code that specifically deals with handling errors and that traditionally deserves its specific documentation can become redundant if you learn to avoid the error cases altogether by using better design and coding practices.

考虑计算逆函数的示例。如果除数为零,则没有结果。这通常是错误管理的情况,但另一种方法是使该函数成为一个总函数,一个适用于所有参数值的函数。在这种情况下,要创建总计函数,您需要使用特殊值 来扩展数字类型NotANumberNotANumber然后,当除数为零时,该函数可以直接返回,而不是走错误管理之路。

Consider the example of a function that calculates an inverse. If the divisor is zero, then there is no result. This is often a case of error management, but an alternative is to make the function a total function, a function that works for the values of all parameters. In this case, to make a function total, you need to extend the number type with the special value NotANumber. Then the function can just return NotANumber when there’s a division by zero instead of taking the error management road.

文档驱动的开发

Documentation-Driven Development

这是一个关于文档的秘密。它不仅仅是阅读有用。写作行为就像测试一样推动质量。

Here’s a secret about documentation. It’s not just useful to read. It’s the act of writing that pushes for quality in the same way as tests.

—推特上的@giorgiosironi

—@giorgiosironi on Twitter

在任何项目中,从关注您的目标最终结果开始通常是一个好主意。通过关注最终,你首先关注价值所在,以确保它确实存在。然后,您可以得出实现目标真正需要的内容(不少也不少),并避免不必要的工作。首先解释您的目标或最终结果,例如您的系统将如何使用,以推动构建并帮助发现早期潜在的不一致。

In any project, it is typically a good idea to start with a focus on the end result you are aiming for. By focusing on the end, you first focus on where the value is, to make sure it’s really there. Then you can derive what’s really necessary to achieve the goal—no less and no more—and avoid unnecessary work. Start by explaining your goal or end result, such as how your system will be used, in order to drive the construction and to help notice early potential inconsistencies.

Chris Matts 在 2011 年伦敦 BDD eXchange 会议上的演讲“从商业价值驱动需求”中,给出了一个关于最典型的英国目标“喝杯茶”的很好的例子。从这个目标开始,您可以推导出对热水、干净的杯子、茶包等的需求。

Chris Matts, in his talk “Driving Requirements from Business Value” at the BDD eXchange conference 2011 in London, gave a great example on the most typical British goal of having a cup of tea. Starting with this goal, you can derive the need for hot water, a clean cup, a tea bag, and so on.

一些开发人员发现从一份文档开始有助于从目标开始。戴夫·巴尔默 (Dave Balmer) 在博客文章中这样说道:

Some developers find that starting with a piece of documentation helps start from the goal. Dave Balmer said this in a blog post:

我可以首先只记录重要的内容。这满足了文档中“在我忘记之前写下来”的部分,并让我有时间在以后的草稿中改进它。6

I can start by documenting only that which is important. That satisfies the “write this down before I forget” part of documentation and frees me up to improve it in later drafts.6

6. Dave Balmer,W​​ebkit 开发博客, https://davebalmer.wordpress.com/2011/03/29/source-code-documentation-javadoc-vs-markdown/

6.Dave Balmer, Webkit Developments blog, https://davebalmer.wordpress.com/2011/03/29/source-code-documentation-javadoc-vs-markdown/

测试驱动开发及其近亲 BDD 通过首先关注所需的行为(作为在开始编码之前编写的测试、场景或示例)来利用这种效应。如果您正在实践 TDD 或 BDD,那么您也已经在进行某种形式的文档驱动开发。

Test-driven development and its close cousin BDD exploit this effect by focusing on the desired behavior first, as a test or a scenario or an example written before starting the coding. If you’re practicing TDD or BDD, you’re already doing a form of documentation-driven development, too.

当不确定性非常高时,在一个想法一开始时,编写自述文件就好像项目已经完成一样,有助于澄清目的并充实期望。一旦以书面形式具体化,想法就会成为更深入审查的对象。可以尽早对它们进行批评、审查并与其他人分享。

When uncertainty is very high, at the very inception of an idea, writing the README file as if the project were already done helps clarify the purpose and flesh out expectations. Once materialized in writing, ideas become objects of deeper scrutiny; they can be criticized, reviewed, and shared with other people early.

如果你独自一人,请过几天再回到这些笔记:当你用新的眼光再次看到它们时,你可以以更客观的方式回顾自己的工作,这要归功于你过去的记录给未来的自己。

If you are alone, just let a few days pass before going back to these notes: When you see them again with a fresh set of eyes, you can review your own work in a more objective fashion, thanks to the documentation from your past self to your future self.

让您保持诚实的文档

Documentation to Keep You Honest

持续改进始于对我们的表现进行诚实的回顾。在项目结束时,我们很容易忘记过去的假设,要么在失败时责怪环境,要么庆幸自己的成功。回顾我们的假设并从中学习,就会有改进的机会。您可能会想,“下次我不会这样假设”或“我会在投入更多时间之前先检查假设。”

Continuous improvement starts with honest retrospectives on how well we have performed. At the end of a project, it is easy to forget about our past assumptions and either blame the environment in the case of failure or congratulate ourselves for the success. Opportunities for improvement occur in looking back at our assumptions to learn from them. You might think, “Next time I will not assume that” or “I will first check the assumption before investing more time.”

因此:尽早记录您的假设和尝试的实验,以便在回顾时获得可靠和诚实的数据。

Therefore: Document early what you assume and the experiments you try in order to have reliable and honest data when it is time for retrospection.

这是一种更加数据驱动的方式。并且有相应的工具!例如,growth.Founders.as 为其创始人增长工具箱提供了一个模板,用于声明您的假设并描述您的实验。

This is a way to be a little more data driven. And there are tools for it! For example, growth.founders.as offers their Founders Growth Toolbox with a template to declare your assumptions and to describe your experiments.

文档驱动与“避免文档”之间的明显矛盾

The Apparent Contradiction Between Documentation Driven and “Avoiding Documentation”

在这个阶段,您可能会对文档驱动和试图避免文档之间的明显矛盾感到困惑,正如第 10 章所提倡的那样。这种矛盾确实是言语含糊的问题。当谈论文档驱动的开发时,即使我们使用“文档这个词,我们并不意味着它是一种在人们之间共享知识的方式。相反,这只是在项目一开始就探索需求的一种廉价方法,然后我们才能转向测试和源代码等更昂贵的材料。

At this stage, you might be confused at the apparent contradiction between being documentation driven and trying to avoid documentation, as advocated in Chapter 10. The contradiction is a matter of ambiguity of words indeed. When talking about documentation-driven development, even though we use the word documentation, we don’t mean it as a way to share knowledge among people. Instead, it’s just a cheap way to explore the requirements at the very inception of a project, before we move on to more expensive material like tests and source code.

基本思想是,在不同的不确定性水平下使用不同的材料是可取的:在项目一开始,对话通常是最好的材料。在早期阶段,纸上的对话、笔记和草图、低保真模型、包含意图和场景的自述文件、REPL 中的代码探索、在没有测试的情况下快速编写代码以及使用脚本或动态语言可能是学习和探索的创意材料。稍后,当事情开始稳定时,另一种带有测试甚至 TDD 的编程语言可能会成为选择的材料。从这个角度来看,早期的文档基本上是一个入门材料。

The fundamental idea is that it can be desirable to use different material at different levels of uncertainty: At the very start of a project, conversations are usually the best material. During the early stages, conversations, notes and sketches on paper, low-fi mockups, a README file with intentions and scenarios, code exploration in a REPL, writing code in a spike without tests, and using a scripting or dynamic language might be the idea materials to learn and explore. A bit later, when things start to stabilize, another programming language with tests and even with TDD might become the material of choice. In this light, documentation early is basically a material to get started.

然而,除此之外,文档不得驱动开发,而必须捕获并帮助呈现系统及其代码无法解释自身的想法和已开发内容(见图 11.2 。目标是使代码尽可能自记录。每当我们无法使代码自记录时,我们就必须采取一些文档工作,但我们将其保持在最低限度。

Apart from this case, however, documentation must not drive the development but must capture and help present ideas and what has been developed that the system and its code cannot explain themselves (see Figure 11.2). Making the code as self-documenting as possible is the goal. Whenever we fail at making the code self-documenting, we have to resort to some documentation effort, but we keep it to the minimum.

一个人物代表一个卡通人物,从环境中探索许多事物,并在另一张图片中捕捉到它想要的东西,并在第三张图片中呈现他们捕捉到的东西。

图 11.2 探索与捕获和呈现

Figure 11.2 Explore versus capture and present

文档驱动避免文档之间并不矛盾。它们只是涉及文档一词的不同含义。

There’s no contradiction between documentation driven and avoiding documentation. They just involve different meanings of the word documentation.

滥用活文档(反模式)

Abusing Living Documentation (Anti-pattern)

因此,您现在是实时文档的粉丝,并且您在每次构建期间都会生成图表。您非常喜欢这个想法,以至于您花时间弄清楚可以生成哪种新类型的图表。你想要生成一切!

So you’re now a fan of living documentation, and you’re generating diagrams during each build. You like the idea so much that you spend your time figuring out what new kind of diagram could be generated. You want to generate everything!

您假装应用 DDD,但实际上您将时间花在生成图表(如果不是代码或字节码)的令人兴奋的工具上。我们都知道 DDD 主要是关于工具,对吧?哦,是的,你记得有些人曾经认真地这样做过,他们称之为 MDA。哎哟!

You pretend to apply DDD, but you actually spend your time on exciting tools that generate diagrams, if not code or bytecode. We all know that DDD is primarily about tools, right? Oh, yes, you remember some folks used to do that seriously, and they called it MDA. Ouch!

您更喜欢使用图表生成器而不是修复生产代码中的错误。当然,这比无聊的制作问题有趣得多!这一切真的是一件好事吗?

You prefer working on the diagram generator rather than fixing bugs in the production code. Of course, it’s way more fun than boring production issues! Is all that really a good thing?

滥用活文档很容易,而且这样做可能会适得其反。如果您花费太多时间使用工具来生成术语表、报告和图表,而不是完成要完成的工作,那么这是不专业的,管理层可能会决定停止并禁止任何文档改进举措。你不想要这样。

It’s easy to abuse living documentation, and doing so can backfire. If you spend too much time using tools to generate glossaries, reports, and diagrams instead of doing the work to be done, it’s not professional, and management may decide to stop and forbid any documentation-improving initiative. You don’t want that.

因此:与实际交付工作相比,在自动化活文档方面所做的努力要保持合理。请记住,活文档只是达到目的的一种手段,而不是目的本身。活文档的目标是帮助交付更多内容并提高质量,而不仅仅是生成文档或享受乐趣。理想情况下,改进活文档的每一项努力都应该在交付、质量或用户满意度方面产生短期明显的好处。

Therefore: Keep your efforts in automating living documentation reasonable compared to the actual delivery work. Remember that living documentation is just a mean to an end and not an end in itself. The goal of living documentation is to help deliver more and with better quality, not just produce documentation or have fun. Ideally, every effort in improving your living documentation should yield short-term demonstrable benefits in delivery, quality, or user satisfaction.

作为这本关于活文档的书的作者,我不希望这个主题因为人们滥用而受到负面报道。请不要说本书要求您将描述的每个示例都落实到您自己的项目中,因为事实并非如此。所有的例子都是例子,而不是要求。

As the author of this book on living documentation, I don’t want this topic to get bad press because people abuse it. Please don’t say that this book is asking you to put in place every example described into your own project, because that’s not true. All the examples are, well, examples, not requirements.

确实,这本书的目的是激发你内心的极客尝试活文档的想法。但我绝不会建议你在没有对书中的每个想法都有充分理由的情况下完成这一切。

It is true that the point of this book is to excite your inner geek to try the ideas of living documentation. But I would never advise you to do it all without a good reason for each of the ideas in the book.

活文档并不是重述 20 世纪 90 年代旧思想的免费许可证。特别要注意以下故障模式,它们不是实时文档:

Living documentation is not a free license to rehash old ideas from the 1990s. In particular, beware of the following failure modes, which are not living documentation:

  • 为最终用户文档制作实时文档:请记住,本书根本不是关于最终用户文档的。某些模式可能适用,但您仍然需要熟练的技术写作才能为最终用户生成高质量的文档。

  • Doing living documentation for end-user documentation: Keep in mind that this book is not about end-user documentation at all. Some patterns may apply, but you still need skilled technical writing in order to produce high-quality documentation for end users.

  • MDA 和所有代码生成:不,代码不是需要替换或生成的脏细节;只要有可能,它就是参考和首选媒体。您应该扩展您的语言或选择更好的编程语言,而不是从图表生成代码。

  • MDA and everything code generation: No, code is not a dirty detail to replace or generate; it is the reference and the preferred media whenever possible. You should extend your language or choose a better programming language rather than generate code from diagrams.

  • 记录一切,甚至自动记录:记录是有成本的,必须权衡其收益。理想的情况是代码具有自我描述性,不需要其他任何东西,但即使如此也不是绝对的。完美和对纯洁的追求往往会导致拖延——应该避免。

  • Documenting everything, even automatically: Documenting has a cost, which must be weighed against the benefits. The ideal case is code being so self-descriptive that it needs nothing else, but even that is not an absolute. Perfection and the quest for purity often amount to procrastination—and should be avoided.

  • 对 UML 的痴迷:一些基本的 UML 是可以的,但它本身并不是目的。选择目标受众能够真正理解的最简单的符号,并尽可能少地进行解释。不要沉迷于通用符号;针对特定问题或特定领域的符号通常更具表现力。

  • UML obsession: Some basic UML is fine, but it is not an end in itself. Chose the simplest notation that the intended audience will really understand with as few explanations as possible. Don’t obsess over generic notations; problem-specific or domain-specific notations are often more expressive.

  • 无处不在的设计模式:了解模式会很有帮助,并且由于它们带来的词汇,您可以使用它们来帮助记录设计。但不要滥用模式。简单性应该是您的首要任务。IF有时两个陈述可能比一个策略模式更好。

  • Design patterns everywhere: Knowing patterns can be helpful, and you can use them to help document a design thanks to the vocabulary they bring. But don’t abuse patterns. Simplicity should be your first priority. Two IF statements might be better than a strategy pattern at times.

  • 分析瘫痪:在做出每个重要的设计决策之前,让整个团队在白板上一起花 15 分钟时间是值得的。花费几个小时甚至几天都是浪费。我鼓励您作为整个团队在白板上启动新功能一段时间,然后快速转向 IDE。下次你面临重大决定时,你可以再次邀请整个团队,除非你喜欢群体编程,这使得整个团队在一起成为永久状态。

  • Analysis paralysis: Having a whole team spend 15 minutes together on the whiteboard before each important design decision is time well spent. Spending many hours or even days is a waste. I encourage you to start new features as a whole team, on the whiteboard for a short while but then moving on quickly to the IDE. And you can just invite the whole team again next time you face a prominent decision, unless you’re into mob programming, which makes the whole team together the permanent state.

  • 活生生的文档杰作:追求完美实际上是一种拖延。请记住,实时文档是帮助交付生产代码的一种手段,而不是相反。

  • Living documentation masterpiece: Aiming for perfection is really a form of procrastination. Keep in mind that living documentation is a means to help deliver production code, not the other way around.

  • 构建之前的文档:文档应该反映实际构建的内容,而不是规定将构建的内容。如果一个项目很有趣,那么没有什么比启动代码更好的了。详细的设计规范是一种浪费。除了本章前面描述的简短声明或文档驱动的自述文件之外,您的团队应该以一种恰到好处、及时的方式集体编码和反思。

  • Documentation before building: Documentation should reflect what’s actually built rather than prescribe what will be built. If a project is interesting, then nothing can beat starting the code. Detailed design specs are a waste. Beyond a short statement or a documentation-driven README as described earlier in this chapter, your team should code and reflect along the way, collectively, in a just-enough, just-in-time fashion.

活文档导致的拖延

Procrastination by Living Documentation

作为开发人员,我们经常想让事情变得比实际需要的更复杂。对于生产代码来说是这样,对于实时文档工具也是如此。

As developers, we are often tempted to make things more complicated than they need to be. This is true for production code, and it also true with living documentation tools.

当日常工作看起来很无聊时,在技术上使其变得更加复杂是一种获得乐趣的好方法。然而,这并不专业。如果您认为自己是一名软件工匠,您就知道您不应该这样做。然而,我们都会时不时地陷入困境,但通常却没有意识到。

When the everyday work looks boring, making it technically more complicated is a great way to have fun. However, it’s not professional. If you consider yourself a software craftsperson, you know you should not be doing that. However, we all fall for it from time to time, usually without being aware of it.

因此:如果您确实需要一个可以享受乐趣并使事情不必要地过于复杂的空间,那么请务必在您的实时文档工具的代码中进行,而不是在您的生产代码中。您和您同事的生活都会因此而变得更好。

Therefore: If you really need a space where you can have fun and make things needlessly overcomplicated, then by all means do it in the code of your living documentation tools, not in your production code. Your life and the life of your colleagues will be better as a result.

我并不是说你应该给你的活文档工具镀金。我只是说,如果你足够幸运,有一些闲暇时间并且想玩,请用你的文档而不是你的代码来做!

I’m not saying that you should gold-plate your living documentation tools. I’m just saying that if you’re lucky enough to have some slack time and want to play, do it with your documentation, not with your code!

可生物降解的文件

Biodegradable Documentation

现在您应该明白,活文档本身并不是目的,而是达到目的的手段。尝试建立实时文档可以揭示有关代码设计或其他方面的问题。这提供了改善根本原因的机会,这首先对项目和产品有利;它还有助于改善您的活文档。反复进行此类改进会导致一系列简化和标准化。最终,一切都变得如此简单和标准,您不再需要文档 - 这将是完美的。

You should understand by now that living documentation is not an end in itself but a means to an end. Trying to set up living documentation can reveal issues about the design or other aspects of your code. This provides an opportunity to improve the root cause, which is above all good for the project and the product; it also helps improve your living documentation. Making such improvements repeatedly leads to a stream of simplifications and standardizations. Eventually, everything becomes so simple and so standard that you don’t need documentation any longer—and that would be perfect.

因此:考虑怎样才能使文档变得不必要。这是你应该移动的方向。

Therefore: Consider what it would take for documentation to become unnecessary. This is the direction you should move.

是否真正达到这一点并不重要,但这必须是目标:活文档计划的目标是达到几乎不需要文档的质量水平。该过程从建立文档工作开始(参见图 11.3),这将揭示一些问题,您可以修复这些问题,从而减少对更多文档的需求,然后根据需要重复。活文档的目标不是最终​​生成大量漂亮的图表和文档。相反,这些文档和图表应被视为解决方案或中间步骤,以获得需要较少文档的更好解决方案。

It does not matter whether you actually reach this point or not, but it has to be the goal: The goal of a living documentation initiative is to achieve the level of quality where documentation is mostly unnecessary. The process starts with setting up a documentation effort (see Figure 11.3), which will reveal some issues, which you fix, reducing the need for more documentation—and you repeat as needed. The goal of living documentation is not to end up with a lot of beautiful generated diagrams and documents. Instead, those documents and diagrams should be considered workaround solutions or intermediate steps toward better solutions that need less documentation.

一个数字表明,活文档的长期目标是让文档变得不必要。 绘制一个代表可生物降解文件的椭圆形。 可生物降解的文档周围出现了诸如无文档、设置实时文档、揭示问题、修复问题、减少文档需求等短语。

图11.3 活文档的长期目标是让文档变得不必要

Figure 11.3 The long-term goal of living documentation is for documentation to become unnecessary

阿罗拉的一位前同事曾经告诉我在银行的一次经历:

One former Arolla colleague once told me of an experience at a bank:

在那家银行,我加入了一个以遵守每项标准为荣的团队。我指的是市场标准,而不是内部标准。结果是我第一天就能够高效工作!由于我了解这些技术及其标准用法,因此我立即熟悉了所有项目周边。不需要文档,没有惊喜,不需要任何特定的定制。

In that bank, I joined a team that took pride in conforming to every standard. I mean market standards, not in-house standards. The result was that I was able to be productive as soon as the first day! Since I knew the technologies and their standard usage, I was immediately familiar on all the project perimeter. No need for documentation, no surprise, no need for any specific customization.

毫无疑问,这确实需要真正的、持续的努力。找出标准,找出解决具体问题同时仍符合标准的方法。这是一种经过深思熟虑的方法,而且对每个人,尤其是新加入者来说,好处都是实实在在的!

Make no mistake, this was taking a real and continuous effort indeed. Find out the standards, find out the way to solve specific issues while still conforming to standards. This was a deliberate approach, and the benefits were real, for everyone but especially for new joiners!

在《学徒模式:有抱负的软件工匠指南》一书中,Dave Hoover 和 Adewale Oshineye 提倡创建反馈循环。7带有生成的图表、术语表、词云或任何其他媒体的实时文档是一个反馈循环,可以帮助您评估您正在做的事情并检查您自己的心理模型。当您的心智模型与生成文档的内容不匹配时,此反馈循环变得特别有用。

In the book Apprenticeship Patterns: Guidance for the Aspiring Software Craftsman, Dave Hoover and Adewale Oshineye advocate creating feedback loops.7 Living documentation with generated diagrams, a glossary, a word cloud, or any other media is a feedback loop that can help you evaluate what you’re doing and check against your own mental model. This feedback loop becomes particularly useful when your mental model and the content of the generated documents don’t match.

7.胡佛、戴夫和阿德沃尔·奥辛内。学徒模式:有抱负的软件工匠指南。加利福尼亚州塞巴斯托波尔:O'Reilly Media, Inc. 2009。

7.Hoover, Dave, and Adewale Oshineye. Apprenticeship Patterns: Guidance for the Aspiring Software Craftsman. Sebastopol, CA: O’Reilly Media, Inc. 2009.

卫生透明

Hygienic Transparency

内部质量指的是代码的质量、设计的质量,更一般地说,指的是从模糊的需求到让人们满意的工作软件的整个过程。内在品质并不意味着满足自我或成为骄傲的源泉;根据定义,它意味着在短期内是经济的。周复一周、年复一年地持续地节省金钱和时间是可取的。

Internal quality refers to the quality of code, the design, and, more generally, the whole process from the nebulous needs to working software that delights people. Internal quality is not meant to satisfy ego or to be a source of pride; by definition, it is meant to be economical beyond the short term. It is desirable for saving money and time sustainably, week after week, year after year.

内在质量的问题在于它是内在的,这意味着你从外面看不到它。这就是为什么在开发人员看来,许多软件系统的内部都很糟糕。像经理和客户这样的非开发人员很难欣赏里面的代码有多糟糕。对他们来说,唯一的提示是缺陷的频率以及新功能交付速度越来越慢的感觉。

The problem with internal quality is that it’s internal, which means you can’t see it from the outside. That’s why, to a developer’s eyes, so many software systems are awful in the inside. Nondevelopers like managers and customers can hardly appreciate how bad the code is inside. The only hints for them are the frequency of defects and the feeling that new features are delivered more and more slowly.

一切提高软件制作透明度的事情都有助于提高其内部质量。当人们看到内部的丑陋时,就会有修复它的压力。

Everything that improves the transparency of how software is made helps improve its internal quality. When people can see the ugliness inside, there’s pressure to fix it.

因此: 让开发人员和非开发人员尽可能看到软件的内部质量。使用活文档、动态图表、代码度量和其他方式以每个人都能欣赏的方式展示内部质量,即使没有任何特定技能。

Therefore: Make the internal quality of software as visible as possible to developers and nondevelopers alike. Use living documents, living diagrams, code metrics, and other means to expose the internal quality in a way that everyone can appreciate, even without any particular skills.

使用所有这些材料来引发对话,并作为解释事情如何以及为什么会这样的支持,并提出改进建议。确保当代码变得更好时,实时文档和其他技术看起来更好。

Use all this material to trigger conversations and as a support to explain how things are and why they are this way and to suggest improvements. Make sure the living documents and other techniques look better when the code gets better.

请记住,有助于使软件更加透明的技术并不能证明内部质量良好,但它们可以在质量不好时突出显示,这很有用。

Keep in mind that the techniques that help make software more transparent can’t prove that the internal quality is good, but they can highlight when it is bad, and that’s useful.

勒·柯布西耶和里波林定律

Le Corbusier and The Law of Ripolin

勒·柯布西耶 (Le Corbusier) 在其《今日装饰艺术》一书中于 1925 年解释了他对 Ripolin(一个以白色油漆而闻名的品牌)的迷恋。在“一层白色涂料:里波林法则”一章中,他想象每个公民都被要求用一层普通的里波林白色油漆替换所有东西(见图 11.4)他的家变得干净了。不再有肮脏、黑暗的角落。一切都按原样显示。然后是内心的清洁……一旦你把 Ripolin 涂在你的墙上,你就会成为自己房子的主人。” 8

Le Corbusier, in his book The Decorative Art of Today, explains in 1925 his fascination for Ripolin, a brand famous for its white paint. In the chapter “A Coat of Whitewash: The Law of Ripolin,” he imagines every citizen being required to replace everything with a plain coat of Ripolin white paint (see Figure 11.4): “His home is made clean. There are no more dirty, dark corners. Everything is shown as it is. Then comes inner cleanness…once you have put Ripolin on your walls you will be the master of your own house.”8

一个代表“在所有东西都漆成白色的房子里立即可见污垢的人物,显示一个人从杯子里洒出饮料。

图11.4 在所有东西都漆成白色的房子里,污垢立即可见

Figure 11.4 In a house with everything painted white, dirt is immediately visible

8.勒·柯布西耶。当今的装饰艺术。麻省理工学院出版社,1987 年。

8.Le Corbusier. The Decorative Art of Today. MIT Press, 1987.

好的文档应该对代码的内部清洁度产生类似的影响——它的设计和任何其他可见的方面,以便人们可以看到它的肮脏方面。

Good documentation should have a similar effect on the inner cleanness of the code—its design and any other aspect that becomes visible so that people can see its dirty facets.

诊断工具

Diagnostic Tools

典型的文档媒体(如图表和术语表)与诊断工具(如指标和词云)之间的界限非常薄弱。

The line is very thin between typical documentation media like diagrams and glossaries and diagnostic tools like metrics and word clouds.

代码中语言的词云

词云是一个非常简单的图表,其中频繁出现的单词比不频繁出现的单词以更大的字体显示。快速断言应用程序真正讨论的内容的一种方法是从源代码中生成词云。

A word cloud is a very simple diagram in which words that occur frequently appear in a bigger font than less frequent words. One way to quickly assert what an application is really talking about is to generate a word cloud out of the source code.

关于您的代码,词云到底能告诉您什么?如果技术词汇占主导地位,那么您就知道代码并没有真正讨论业务领域(见图11.5)。另一方面,如果领域语言占主导地位(参见图 11.6),那么您一定会做得更好。

What does a word cloud really tell you about your code? If technical words dominate, then you know the code does not really talk about the business domain (see Figure 11.5). On the other hand, if the domain language is dominant (see Figure 11.6), you must be doing a better job.

一个数字代表一个词云,在其中找到业务领域是很困难的。

图11.5 通过这个词云,要么你的业务域是字符串操作,要么它在源代码中不可见

Figure 11.5 With this word cloud, either your business domain is on string manipulation, or it’s not visible in the source code

一个数字代表一个词云,其中可以看到 Flottio 加油卡和车队管理的语言。

图11.6 在这个词云中,你可以清楚地看到Flottio加油卡和车队管理的语言

Figure 11.6 In this word cloud, you can clearly see the language of Flottio fuel cards and fleet management

用源代码创建词云并不困难;您甚至不必解析源代码,只需将其视为纯文本并过滤编程语言关键字和标点符号,如下所示:

Creating a word cloud out of source code is not difficult; you don’t even have to parse the source code but can simply consider it as plain text and filter the programming language keywords and punctuation, like this:

1 // 从源码根文件夹开始,递归走
2 遍历所有 *.java 文件(分别为 C# 中的 *.cs 文件)
3
4 // 对于作为字符串读取的每个文件,按语言分割
5 个分隔符(您也可以考虑使用驼峰命名法分隔):
6
7
8 个分隔符 = ";:.,?!<><=+-^&|*/\" \r\n {}[]()"
9
10 // 忽略以“@”开头的数字和标记,或者
编程语言的 11 个关键字和停用词:
12 关键字 = { "摘要", "继续", "为", "新",
13 “开关”,“断言”,“默认”,“如果”,“包”,“布尔”,
14 “do”、“goto”、“private”、“this”、“break”、“double”、
15 “实现”、“受保护”、“抛出”、“字节”、“其他”、
16 “导入”、“公共”、“抛出”、“案例”、“枚举”、
17 "instanceof", "return", "transient", "catch", "extends",
18 "int", "", "short", "try", "char", "final", "interface",
19 "static", "void", "class", "finally", "long", "strictfp",
20 “易失性”,“常量”,“浮动”,“本机”,“超级”,“同时”}
21
22
23STOPWORDS = { "the", "it","is", "to", "with", "what's",
24 “由”、“或”、“和”、“两者”、“是”、“的”、“在”、“obj”、
25 “字符串”,“哈希码”,“等于”,“其他”,“tostring”,
26 "假", "真", "对象", "注释" }
24
1 // From the root folder of the source code, walk recursively
2 through all *.java files (respectively *.cs files in C#)
3
4 // For each file read as a string, split by the language
5 separators (you may consider to split by CamelCase too):
6
7
8 SEPARATORS = ";:.,?!<><=+-^&|*/\" \r\n {}[]()"
9
10 // Ignore numbers and tokens starting with '@', or that are
11 keywords and stopwords for the programming language:
12 KEYWORDS = { "abstract", "continue", "for", "new",
13 "switch", "assert", "default", "if", "package", "boolean",
14 "do", "goto", "private", "this", " break", "double",
15 "implements", "protected", "throw", "byte", "else",
16 "import", "public", "throws", "case", "enum",
17 "instanceof", "return", "transient", "catch", "extends",
18 "int", "",  "short", "try", "char", "final", "interface",
19 "static", "void", "class", "finally", "long", "strictfp",
20 "volatile", "const", "float", "native", "super", "while" }
21
22
23 STOPWORDS = { "the", "it","is", "to", "with", "what's",
24 "by", "or", "and", "both", "be", "of", "in", "obj",
25 "string", "hashcode", "equals", "other", "tostring",
26 "false", "true", "object", "annotations" }
24

此时,您可以打印每个未过滤的标记,然后将控制台复制/粘贴到在线词云生成器(例如 Wordle.com)中。

At this point, you could just print every token that was not filtered, and copy/paste the console into an online word cloud generator such as Wordle.com.

您还可以使用 a bag(即 Guava 的多重集)自行计算标记的出现次数:

You can also count the occurrences of tokens yourself by using a bag (that is, a multiset from Guava):

1 个 bag.add(token)
1  bag.add(token)

您可以通过将单词数据转储到页面中,使用 d3.layout.cloud.js 库在 HTML 页面中呈现单词云。

You could render the word cloud within an HTML page with the d3.layout.cloud.js library by dumping the word data into the page.

代码形状的签名调查

另一个从源代码中可视化代码设计的低技术想法是 Ward Cunningham 提出的签名调查。9这个想法是过滤掉除语言标点符号(逗号、引号和括号)之外的所有内容,作为对源代码文件的纯字符串操作。

Another low-tech idea for visualizing the design of code out of the source code is the signature survey proposed by Ward Cunningham.9 The idea is to filter out everything except the language punctuation (commas, quotation marks, and brackets) as pure string manipulation on the source code files.

9. Ward Cunningham,“签名调查:浏览不熟悉代码的方法”, http://c2.com/doc/SignatureSurvey/

9.Ward Cunningham, “Signature Survey: A Method for Browsing Unfamiliar Code,” http://c2.com/doc/SignatureSurvey/

例如,考虑一下这个签名调查,它分为三大类:

For example, consider this signature survey, which has three big classes:

BillMain.java ;;;;;;;;;;;;;{;;{"";;"";;{"";"";}
{;;{;;}};;;;{{;;;{;;}{;;};;;}}{;}"";}{;}{;;"";"
";;;"";"";;;"";"";";;";;;"";"";;;"";"";;;};;{;{
""{;}""{;}""{;}""{;}""{;;;;;;}""{;}""{;}""{;};"
"{;;;;;}""{;;;;;}};}{;;;;;""{"";"";;}""{"";"";;
""{;}}""{"";"";;""{;}};{;}""{;}{;};;;;;}{;;;;;;
}{;;;;;;}{;""{;{;}{;};;}{;{;}{;};;};}{{"";}{"""
";}{"";};;{;}{"";};}{{;};";";;;{""{{"";};}}{{;;
;}}{;};}{;{;}";";;;{""{{"";};}}{;;{""{{"";}""{;
}{;}}}};{;;;}{"";;;;;;;}}{;{;}{;};}{;""{;}{;};
}{;{{"";};}{{"";};};}{;;;;;;;;;{{"";};;}{{"";};
;;};}{;;;;;;;;;{;;;{"";}{{"";};}{{"";};;};}\;}{
;;""{;}{;};}{;;{""{"";}{"";};}{;}{{{;}{;}}};}}
CallsImporter.java ;;;;;;;{;;{{"";};{;;"";;;{;}
{;;{;};};{;"";{;;};;{;;{;};}{;}{;};}}{;}{{{;}{;
}}}}{""{;}""{;}""{;}""{;}""{;}""{;}""{;}""{;}""
{;}""{;}""{;}""{;}""{;}""{;}""{;}""{;}""{;}""{;
}""{;}""{;}""{;}""{;}""{;}""{;}""{;}""{;}""{;}"
"{;}""{;}""{;}""{;}""{;};}}
UserContract.java ;;{;;;;;{;}{;}{;}{;}{;}{;}{;}
{;}{;}{;}}}{{""
BillMain.java ;;;;;;;;;;;;;{;;{"";;"";;{"";"";}
{;;{;;}};;;;{{;;;{;;}{;;};;;}}{;}"";}{;}{;;"";"
";;;"";"";;;"";"";";;";;;"";"";;;"";"";;;};;{;{
""{;}""{;}""{;}""{;}""{;;;;;;}""{;}""{;}""{;};"
"{;;;;;}""{;;;;;}};}{;;;;;""{"";"";;}""{"";"";;
""{;}}""{"";"";;""{;}};{;}""{;}{;};;;;;}{;;;;;;
}{;;;;;;}{;""{;{;}{;};;}{;{;}{;};;};}{{"";}{"""
";}{"";};;{;}{"";};}{{;};";";;;{""{{"";};}}{{;;
;}}{;};}{;{;}";";;;{""{{"";};}}{;;{""{{"";}""{;
}{;}}}};{;;;}{"";;;;;;;;}}{;{;}{;};}{;""{;}{;};
}{;{{"";};}{{"";};};}{;;;;;;;;;{{"";};;}{{"";};
;;};}{;;;;;;;;;{;;;{"";}{{"";};}{{"";};;};}\;}{
;;""{;}{;};}{;;{""{"";}{"";};}{;}{{{;}{;}}};}}
CallsImporter.java ;;;;;;;{;;{{"";};{;;"";;;{;}
{;;{;};};{;"";{;;};;{;;{;};}{;}{;};}}{;}{{{;}{;
}}}}{""{;}""{;}""{;}""{;}""{;}""{;}""{;}""{;}""
{;}""{;}""{;}""{;}""{;}""{;}""{;}""{;}""{;}""{;
}""{;}""{;}""{;}""{;}""{;}""{;}""{;}""{;}""{;}"
"{;}""{;}""{;}""{;}""{;};}}
UserContract.java ;;{;;;;;{;}{;}{;}{;}{;}{;}{;}
{;}{;}{;}}}{{""

现在将上面的签名调查与此进行比较,后者的作用完全相同,但班级规模更小:

Now compare the signature survey above with this one, which does exactly the same thing but with more smaller classes:

AllContracts.java ;;;;;{;{;}{{;}}{"""";}}
BillingService.java ;;;;;;;{;{"";}{;;;;;}{""
;;}{;;"";}{;}{;}{"";;;;;}{"";;}{;;{{;;}};}{;}}
BillPlusMain.java ;;;;;;{{;"";"";"";"";"";"";}}
Config.java ;;;;;;;{;{;{"";;}{;}{{{;}{;}}}}{;}{
;;}{"";}{"";}{"";}{"";}{"";;}{;";";{;};}}
Contract.java ;;;;{;;;;{;;;;}{;}{;}{;}{;}{;}{"""""""""";}}
ContractCode.java ;{"""""""""""""""""""""""";;{;}{;}}
ImportUserConsumation.java ;;;;;{;;{;;}{{;}{;}}{;{;;}}
{;"";;;;{;};}{{;}{;}}{"";;;{;{;}};}}
OptionCode.java ;{"""""""";;{;}{;}}
Payment.java ;;;{;;;{;;;{"";}}{;}{;}{;}{{;}"";}{;}{;;;;;}{;}
{"""""";}}
PaymentScheduling.java ;;;;{{{;;;}}{{;;;}}{{;;;}};{;;;;}
{;;{;};;}{;;;;;;;;}{;}}
PaymentSequence.java ;;;;;;{;;{;}{;;}{;}{;}{;;;}{"";}}
用户消费.java ;{;;{;;}{;}{;}}
用户消费跟踪.java ;{{;}{;}}
AllContracts.java ;;;;;{;{;}{{;}}{"""";}}
BillingService.java ;;;;;;;{;{"";}{;;;;;}{""
;;}{;;"";}{;}{;}{"";;;;;}{"";;}{;;{{;;}};}{;}}
BillPlusMain.java ;;;;;;{{;"";"";"";"";"";"";}}
Config.java ;;;;;;;{;{;{"";;}{;}{{{;}{;}}}}{;}{
;;}{"";}{"";}{"";}{"";}{"";;}{;";";{;};}}
Contract.java ;;;;{;;;;{;;;;}{;}{;}{;}{;}{;}{"""""""""";}}
ContractCode.java ;{"""""""""""""""""""""""";;{;}{;}}
ImportUserConsumption.java ;;;;;{;;{;;}{{;}{;}}{;{;;}}
{;"";;;;{;};}{{;}{;}}{"";;;{;{;}};}}
OptionCode.java ;{"""""""";;{;}{;}}
Payment.java ;;;{;;;{;;;{"";}}{;}{;}{;}{{;}"";}{;}{;;;;;}{;}
{"""""";}}
PaymentScheduling.java ;;;;{{{;;;}}{{;;;}}{{;;;}};{;;;;}
{;;{;};;}{;;;;;;;;}{;}}
PaymentSequence.java ;;;;;;{;;{;}{;;}{;}{;}{;;;}{"";}}
UserConsumption.java ;{;;{;;}{;}{;}}
UserConsumptionTracking.java ;{{;}{;}}

您更喜欢从事哪一项工作?

Which one do you prefer to work on?

可以想象类似的技术含量低但有用的纯文本可视化方法。如果您有任何想法,请告诉我。

It’s possible to imagine similarly low-tech yet useful plain-text visualization approaches. Let me know if you have any ideas.

正压清洁内部

Positive Pressure to Clean the Inside

软件开发领域的一个巨大问题是,对于管理预算和做出重大决策的人员来说,内部质量根本不可见,例如对开发人员说“是”或“否”、与另一家公司签订合同或离岸外包。缺乏洞察力阻碍了这些人做出明智的明智决策。相反,它促进那些在论点上更有说服力和吸引力的人做出决定。

One huge issue in the field of software development is that internal quality is not visible at all for the people who manage budgets and make the biggest decisions, such as saying yes or no to developers, contracting to another company, or offshoring. This lack of insight impedes these people from making good, informed decisions. Instead, it promotes decisions from people who are more convincing and seductive in their arguments.

当开发人员能够以非技术人员可以从情感上理解的方式展示代码的内部质量时,他们就会变得更有说服力。即使对于非开发人员来说,完全混乱的词云或依赖关系图也很容易解释。一旦他们自己理解了视觉上显示的问题,谈论补救措施就会变得更容易。

Developers can become more convincing when they can show the internal quality of code in a way that nontechnical people can apprehend emotionally. A  word cloud or a dependency diagram that is a total mess is easy to interpret even by nondevelopers. Once they understand by themselves the problem shown visually, it becomes easier to talk about remediation.

开发人员的意见常常引起管理者的怀疑。相比之下,管理者欣赏工具的输出,因为工具是中立和客观的(或者至少他们相信情况是这样)。工具绝对不是客观的,但它们确实呈现了真实的事实,即使呈现总是带有一些偏见。

Developers’ opinions are often suspect to managers. In contrast, managers appreciate the output of tools because tools are neutral and objective (or at least they believe this is the case). Tools are definitely not objective, but they do present actual facts, even if the presentation always carries some bias.

活文档背后的想法不仅仅是为团队提供文档,而且成为用于说服他人的工具箱的一部分。当每个人都能看到令人不安的现实——混乱、循环依赖、难以忍受的耦合、代码的模糊性——就变得更难以容忍这一切。

The ideas behind living documentation are not just to document for the team but to be part of the toolbox used to convince others. When everyone can see the disturbing reality—the mess, the cyclical dependencies, the unbearable coupling, the obscurity of the code—it becomes harder to tolerate it all.

图片哈哈

LOL

尊重非循环依赖原则:只有一个包!

To respect the acyclic dependencies principle: Have only one package!

活文档包括让每个人都能看到代码的内部问题,创造积极的压力,鼓励清理内部质量。

Living documentation involves making the internal problems of code visible to everyone, creating positive pressure that encourages cleanup of the internal quality.

设计技巧无处不在

Design Skills Everywhere

即使您以解决文档问题为目标开始了活文档之旅,您很快就会发现您的实际问题是设计很差或任意,可能是“巧合设计”的结果。要解决文档问题,您必须解决设计问题。这确实是个好消息!

Even if you start a living documentation journey with the goal of solving the documentation problem, you’ll quickly discover that your actual problem is that the design is poor or arbitrary, perhaps a result of “design by coincidence.” To solve the documentation problems, you have to solve the design problems. This is all good news indeed!

通过对文档的关注,您最终会得到具体的可见标准,让每个人都能看到设计当前状态的混乱情况。然后就会有改进设计的积极压力,其好处远远超过文档的明显好处。但正如之前提到的,还有更多好消息:良好的设计技能也可以造就良好的活文档技能。专注于实时文档并专注于软件设计技能。两者一起练习,一切都会变得更好!

Through the focus on documentation, you end up with concrete visible criteria for everyone to see the big mess that is the current state of the design. There is then positive pressure to improve the design, which has benefits that well exceed the obvious benefits of documentation. But as mentioned before, there’s even more good news: Good design skills also make good living documentation skills. Focus on living documentation and focus on software design skills. Practice both together, and everything will get better!

软件设计涉及仔细决定对相同行为进行编码的所有可能方法。或者,用杰里米·查萨恩 (Jeremie Chassaing) 的话来说,“它是在无数种可能性中选择一个有充分理由的可能性。” 10 “但最后还是一样!” 是一个在设计讨论期间经常遭到反对。是的。如果您只关心使代码正常工作,那么设计就无关紧要。设计意味着关心问题而不仅仅是使其发挥作用。

Software design involves deciding carefully among all the possible ways to code the same behavior. Or, in Jeremie Chassaing’s words, “It’s picking one in gazillions of possibilities with good reasons.”10 “But it’s the same thing at the end!” is a frequent objection during design discussions. Yes, it is. If you just care about making the code work, design is irrelevant. Design means caring about concerns beyond just making it work.

10.在推特上@thinkb4coding https://twitter.com/thinkb4coding/status/837250039688933376

10.On Twitter @thinkb4coding https://twitter.com/thinkb4coding/status/837250039688933376

设计技能包括考虑耦合和内聚、隐藏实现细节、考虑契约和数据治理、为以后保留选项、最小化依赖性以及处理它们的相对稳定性等。

Design skills include thinking about coupling and cohesion, hiding implementation details, considering contracts and the governance of data, keeping options open for later, minimizing dependencies, and dealing with their relative stability, among other things.

记者波特采访Living Doc Doc先生

Reporter Porter Interviewing Mr. Living Doc Doc

以下为记者Porter(图11.7右侧)对生活文献专家Living Doc Doc(图11.7左侧)的采访。

The following is an interview Reporter Porter (on the right in Figure 11.7) conducted with Living Doc Doc, an expert on living documentation (on the left in Figure 11.7).

图中显示了两个卡通人物,其中一个是记者,正在采访另一个。

图11.7 记者Porter采访的Living Doc Doc

Figure 11.7 Living Doc Doc interviewed by Reporter Porter

什么是好的文档?

What is good documentation?

最好的文档是这样的代码,它使所有内容都变得如此明显,让您立即理解它,并且命名非常好,可以立即清晰。好的文档如此集成在工作流程和日常工具中,以至于您甚至不认为它是文档。一个引人注目的例子是,当您需要某个工具时,它会提醒您忘记或不知道的事情。我们通常不称之为文档,但在正确的时间提供正确的知识的最终目的确实是文档的一种形式。

The best documentation is code that makes everything so obvious that you understand it immediately, with naming that is so good it’s instantly clear. Good documentation is so integrated in the workflow and into the daily tools that you don’t even think about it as being documentation. One striking example is when a tool reminds you of something you forgot or didn’t know right when you need it. We usually don’t call that documentation, but the end purpose of bringing the right piece of knowledge at the right time really is a form of documentation.

为什么活的文档不受欢迎?

Why is living documentation not popular?

我认为许多做法很流行,但没有人注意到。还记得 2000 年代初期对 UML 的所有关注吗?现在项目越来越大,而我们很少使用 UML。相反,每个 IDE 都提供即时、集成和高度上下文的类型层次结构树、轮廓、类之间平滑的超媒体导航……所有这些都比数百个静态 UML 图更有用。尽管如此,我们仍然认为这是理所当然的,并且仍然对“缺乏适当的文件”感到难过。还有新技术。

I think many of the practices are popular, but nobody noticed. Remember all the focus on UML in the early 2000s? Now projects are bigger today, and we don’t use UML much. Instead, every IDE offers instant, integrated, and highly contextual type hierarchy trees, outlines, smooth hypermedia navigation between classes,… and all this is more useful than hundreds of static UML diagrams. Still, we take it for granted and still feel bad about the “lack of proper documentation.” And there are new technologies as well.

新技术如何改变了现状?

How have new technologies changed the picture?

大多数人仍然没有意识到新工具和实践在知识转移方面的全部潜力。

Most people still haven’t realized all the potential of newer tools and practices when it comes to transferring knowledge.

Consul 和 Zipkin 提供了实际情况的实时回顾,甚至是活生生的图表。他们提供了一个标签机制来定制和传达意图。

Consul and Zipkin offer live recaps of what’s actually there, even as living diagrams. They offer a tag mechanism to customize and convey intents.

使用阈值监控关键 SLA 指标使我们接近记录 SLA。

Monitoring of key SLA metrics with thresholds gets us close to documenting the SLA.

Puppet、Ansible 和 Docker 文件允许使用声明式风格来描述您的期望。想象一下它们可以有利地替换的所有 Word 文档!

Puppet, Ansible, and Docker files allow for a declarative style for describing what you expect. Imagine all the Word documents they advantageously replace!

那么你现在不需要做任何特别的事情吗?

So you need not do anything special now?

几乎。但也不完全是。所有新技术和实践都非常适合记录什么如何但大多数地方的弱点仍然是基本原理和原因,而这往往被遗忘。这就是为什么您仍然需要找到一种方法来记录每个主要决策的理由。不可变的仅附加日志、带有标签的代码增强以及传统文档中的一些常绿内容对于整体愿景来说对于完成图片来说是非常宝贵的。

Almost. But not totally. All the new technologies and practices are fantastic for documenting the what and the how, but the weak point mostly everywhere remains the rationale, the why, which is often forgotten. That’s why you still need to find a way to record the rationale for each of the main decisions. An immutable append-only log, code augmentation with tags, and a few evergreen content in traditional documents for the overall vision can be invaluable to complete the picture.

那么代码呢?

And what about the code?

代码应该尽可能地自我记录。测试和业务可读的场景是记录知识的重要组成部分。但有时您必须添加额外的代码,以便将您的设计决策和意图记录在相应的代码中;文档和命名约定的自定义注释是您在这里选择的工具。

Code should be self-documenting as much as possible. Tests and business-readable scenarios are an important part of this recorded knowledge. But sometimes you have to add extra code just to record your design decision and intention right inside the corresponding code; custom annotations for documentation and naming conventions are your tools of choice here.

好的,但是现在的系统由数十种服务组成。我该如何处理这些分散的系统?

Okay, but these days systems are made of dozens of services. How do I work with such fragmented systems?

您只需应用相同的技术,但级别不同。例如,注释成为服务注册表和分布式跟踪系统中的标签。包和模块的命名约定成为服务及其端点的命名约定。它涉及相似的思维和相似的设计技巧,但不同的实现。

You just apply the same techniques but at a different level. For example, annotations become tags in your service registry and in your distributed tracing system. Naming conventions of packages and modules become naming conventions of services and their endpoints. It involves similar thinking and similar design skills but different implementation.

我们真的需要文档吗?多年来,我们一直在很少或没有任何文件的情况下生活,但我们仍然活着!

Do we really need documentation? We’ve been living with little or no documentation for years, and we’re still alive!

当然,我们可以在没有明确文档的情况下生活。任何人都可以采用一个未知的系统并使其发挥作用——至少在某种工作定义下是这样。但仅仅“让它发挥作用”的门槛很低,而且“让它发挥作用”可能需要很多时间。文档可以加速交付,因为它缩短了重建系统思维模型的时间。但文档的另一个作用是,尝试记录有关系统的知识是了解系统错误的好方法。显然,关注文档是以后的投资,但不太明显的是,现在也有回报!

Of course, we can live without express documentation. Anyone can take an unknown system and make it work—at least under some definition of work. But just “making it work” is a very low bar, and “making it work” may take a lot of time. Documentation accelerates delivery because it shortens the time to rebuild your mental model of the system to work on. But the other effect of documentation is that trying to record the knowledge about the system is a great way to learn what’s not right about the system. Paying attention to documentation is an investment for later, obviously, but less obviously there’s also a return for right now!

非常感谢!

Thank you very much!

不,非常感谢!

No, thank you very much!

概括

Summary

整本书的主要论点是,如果你从文档开始,你最终会得到更好的设计。

The main thesis of this book as a whole is that if you start with documentation, you end up with better design.

大多数团队最初出于非回归测试目的而采用 BDD,最终意识到更大的好处在其他地方 - 在使用具体示例的早期对话中以及生成的实时文档中。同样,通过重新考虑文档;采用鼓励速度、审慎、卫生透明度和人与人之间互动的做法;通过在整个过程中倾听信号,好事就会发生。

Most teams initially embrace BDD for non-regression testing purposes and end up realizing that the bigger benefits are somewhere else—in the early conversations using concrete examples and in the resulting living documentation. Similarly, by reconsidering documentation; adopting practices that encourage speed, deliberateness, hygienic transparency, and interactions between people; and by listening to the signals throughout this process, good things happen.

第12章

Chapter 12

生活建筑文档

Living Architecture Documentation

架构可以通过多种方式定义:“架构是项目中的每个人都应该知道的”或“架构是重要的,无论是什么”或“架构是关于以后很难改变的决策”。所有这些定义都暗示着架构涉及随着时间的推移在多人之间交换有关某些决策的知识。这些决定不是孤立的事件,而是在当时的背景下做出的决定。

Architecture can be defined in many ways: “Architecture is what everybody in the project should know” or “Architecture is what is important, whatever that is” or “Architecture is about the decisions that are hard to change later.” What is implied in all these definitions is that architecture involves exchanging knowledge about some decisions between multiple people over time. These decisions are not isolated events but are decisions in the context at the time.

因此文档是架构的重要组成部分。已经提出了许多文档方法,并且有许多关于该主题的书籍。本章重点介绍活文档如何帮助架构,特别是在实践演化架构的团队的背景下,架构随时可能发生变化。

So documentation is a significant part of architecture. A number of documentation approaches have been proposed, and there are many books on this topic. This chapter focuses on how living documentation can help with architecture, especially in the context of teams practicing evolutionary architecture, where the architecture can change all the time.

从这个角度来看,建筑不是一个阶段,而是一个持续的活动。而且,它不一定只由建筑师来完成;相反,它是任何有能力做到这一点的软件开发人员的领域。这就需要更多的人分享架构决策。

In this view, architecture is not a phase but a continuous activity. Moreover, it is not necessarily performed only by architects; rather, it is the domain of any software developer who has the skills to do it. This creates a need for even more people to share architectural decisions.

软件架构通常最终会在多个地方具体化为代码。该代码是过去架构决策的结果。只需查看代码库,您就可以认识到许多过去的决定。凭借正确的技能,您只需注意代码库中的巧合,就可以识别甚至逆向工程许多过去的决策:您可能会意识到,“它不可能是偶然结构良好的,所以它一定是为此而设计。” 决定就在那里,即使它们是隐含的。

A software architecture usually ends up being materialized as code in multiple places. This code is the consequence of the past architectural decisions. You can recognize many of these past decisions just by looking at the code base. With the right skills, you can recognize, or even reverse engineer, many of the past decisions just by noticing the happy coincidences in the code base: You might realize, “It cannot be that well-structured by chance, so it must have been designed for that.” The decisions are there, even if they are implicit.

凭借通常从经验中获得的正确技能,您可以从设计中了解它是什么以及预期如何扩展。这类似于图 12.1中的电源板,如果您只需将额外的电线插入其中,则显然可以进行扩展。

With the right skills, usually grown from experience, you can read from the design what it is and how it is expected to be extended. This is similar to the power strip in Figure 12.1 that is visibly ready for extension if you just plug additional cords into it.

图中显示了一个接线盒,其中使用了两个插座。 其他三个未使用的套接字表示“显然已准备好扩展”

图 12.1 明显准备好扩展

Figure 12.1 Visibly ready for extension

但是,由于代码中的决策是隐式的,因此仅通过查看代码,您可能会错过很多架构意图,具体取决于您对所使用的样式的熟悉程度。帮助您和其他人发现更多的架构意图是架构文档的主要目标之一:您希望使隐含的显式化!

However, because the decisions in code are implicit, by just looking at the code, you may miss much of the architectural intent, depending on how familiar you are with the styles being used. Helping you and others discover more of the architectural intent is one major goal of architectural documentation: You want to make the implicit explicit!

架构的活文档恰恰是寻找有助于准确、明确地解释更多决策的实践,而不会减慢预期和鼓励的持续变化流程。

Living documentation for architecture is precisely about finding practices that help explain more decisions accurately and explicitly, without slowing down the continuous flow of changes that are expected and encouraged.

本书到目前为止的章节已经提到了架构方面的许多示例,例如动态图和六边形架构、上下文图、导游、代码即文档以及一些强制指南示例。本章对所有内容进行了扩展,并致力于将活文档应用到软件架构中。

The chapters so far in this book have mentioned a number of examples of architectural aspects, such as living diagrams and hexagonal architecture, context diagrams, guided tours, code as documentation, and some enforced guideline examples. This chapter expands on all that and is dedicated to applying living documentation to software architecture.

记录问题

Documenting the Problem

架构总是从真正理解需要解决的问题的所有目标和约束开始。对于一个在街上拥有 50 个热狗摊的品牌,您不会为世界各地 1,500 个高端三明治和沙拉店建立相同的销售点系统,即使它们具有相同的高级基本设施特征。

Architecture always starts with really understanding all the objectives and constraints of the problem that needs to be solved. You won’t build the same point-of-sale system for a brand with 50 hot dog stands in the street as you would for 1,500 high-end sandwich and salad shops around the world, even if they have the same high-level basic features.

高层目标和主要约束是“每个人都应该知道的事情”(见图12.2),因此它们始终是架构的一部分。

The high-level goals and the main constraints are “things that everybody should know” (see Figure 12.2), and as such they are always part of the architecture.

图中显示了一个扬声器。

图 12.2 架构是关于每个人都应该知道的事情

Figure 12.2 Architecture is about things that everybody should know

因此:无论您对架构的定义如何,请确保将其视为技术挑战和文档挑战。将讨论和书面记录重点放在要解决的问题上,而不仅仅是解决方案。确保以持久的形式很好地描述了有关问题的基本知识,并确保每个人都牢记在心。

Therefore: Whatever your definition of architecture, make sure it is considered as much a documentation challenge as a technical one. Focus discussions and written records on the problems to solve, not just on the solutions. Make sure the essential knowledge about the problem is well described in a persistent form, and ensure that it is in everybody’s mind.

您可能会不时地提出随机问题,以检查每个参与人员是否都了解必要的业务知识。我经常喜欢这样做,以确保我们不会在每次讨论中浪费大量时间。

You might ask random questions from time to time to check whether everyone involved knows about the essential business knowledge. I regularly like to do this to make sure we don’t waste a lot of time in every discussion.

请记住,书面形式的文档是远远不够的。不是每个人都会读它。您需要通过随机讨论和路演来补充书面文档,以便在正式​​工作时间将其呈现给每个团队。

Keep in mind that a written form of documentation is never enough; not everyone will read it. You need to complement written documentation with random discussions and roadshows to present it to every team during official work time.

问题简介的示例

An Example of a Problem Brief

这是一个示例,灵感来自于我的一位客户的遗留系统上的真实项目。该简介不在 wiki 中,而是在新组件源代码存储库根目录下的单个 Markdown 文本文件中。它甚至可能位于自述文件中。

Here is an example inspired by a real-world project on a legacy system for one of my customers. The brief is not in the wiki but in a single Markdown text file at the root of the source code repository of the new component. It may even be in the README file.

愿景声明

Vision Statement

日期: 01/06/2015

Date: 01/06/2015

通过出色的 UI 和频繁提供的新功能来取悦用户

Delight the users with great UIs and new features delivered frequently

描述

Description

INSURANCE 2020 计划旨在改进支持保险索赔管理流程的遗留软件,有两个主要目标:

The INSURANCE 2020 program aims at revamping the legacy software supporting the insurance claim management processes, with two main goals in target:

  1. 用户体验 (UX) 和用户友好性 UI

  2. User experience (UX) and user-friendliness UIs

  3. 持续交付:缩短上市时间并降低变更成本

  4. Continuous delivery: reduced time to market and reduced cost of change

利益相关者

Stakeholders

主要利益相关者是保险理算人。其他利益相关者包括:

The primary stakeholders are the insurance adjusters. Other stakeholders are:

  • 精算师

  • Actuaries

  • 管理

  • Management

IT 相关利益相关者包括:

IT-related stakeholders are:

  • 开发团队

  • Development team

  • 中央建筑组

  • Central architecture group

  • 支持和运营团队

  • Support and Ops teams

业务领域

Business Domain

业务领域重点关注理赔管理部分,特别是理赔调整阶段。这从早期提到索赔开始,开始每一项必要的调查,以计划、见证损失、联系警察、律师,以便提出向保单持有人提供的金钱金额。

The business domain focuses on the claim management part, and in particular the Claim Adjustment phase. This starts at the early mention of a claim to start every investigation necessary to plan, witness the damages, contact the police officers, lawyer in order to propose a monetary amount to give to the policy holder.

主要业务能力包括,例如:

The main business capabilities include, for example, the following:

  • 记下没有太多相关信息的索赔

  • Take note of a claim without much information about it

  • 只要有更多信息可用,就可以丰富索赔内容:涉及各方、检查、证据、照片……

  • Enrich the claim whenever more information is available: parties involved, checks, evidences, photographs…

  • 通过一项或多项和解要约准备索赔(每个和解要约由一项或多项金额组成)

  • Prepare the claim with one or more settlement offer(s) (each made of one or more monetary amount(s))

  • 管理索赔团队和相关工作流程

  • Manage the claim team and the related workflows

  • 报告一项或所有待决索赔的当前状态

  • Report the current state of one or all pending claims

  • 帮助用户随时查看自己要做的任务

  • Help users see their tasks to do at any time

显式质量属性

Explicit Quality Attributes

在软件中,质量属性决定了解决方案。对于数百万并发用户来说,给定业务问题的技术解决方案将与它完全不同。对于 100 个并发用户,实时解决方案将与日常使用的解决方案有很大不同,或者如果每分钟的停机时间使公司损失 500,000 美元。

In software, quality attributes shape the solution. The technical solution to a given business problem would be radically different for millions of concurrent users than it would be for 100 concurrent users, and the solution for real time would be quite different from the solution for daily use or if each minute of downtime cost the company $500,000.

由于所涉及的挑战,团队中的每个人都应该意识到最具挑战性的质量属性。他们还应该明白,其他不那么具有挑战性的质量属性提供了保持架构简单的机会。假装你的设计应该支持数百万并发用户,而实际上只有数千用户,这是对赞助商金钱和时间的危险滥用。

Because of the challenges involved, everybody on a team should be aware of the most challenging quality attributes. They should also understand that other quality attributes that are not as challenging present opportunities to keep the architecture simple. Pretending that your design should support millions of concurrent users when you really have only thousands is a dangerous misuse of the sponsor’s money and time.

因此:在项目开始时和每次环境变化后,以书面形式阐明主要质量属性。这可以像要点列表一样简单。明确如何解释质量属性,例如使用格言作为指导。

Therefore: At the start of a project and after each change of context, clarify the main quality attributes in writing. This can be as simple as a list of bullet points. Make it clear how to interpret the quality attributes, such as by using maxims as guidance.

以下是描述主要质量属性的示例:“系统应在 98% 的交易中以 1 秒的响应时间响应用户请求。系统应支持3000个并发用户。”

The following is an example of describing the main quality attributes: “The system shall respond to user requests with a response time within 1 second for 98% of the transactions. The system shall support 3,000 concurrent users.”

《站点可靠性工程》一书,特别是“服务级别目标”一章,对质量属性进行了深入讨论,介绍了服务级别指标、服务级别目标和服务级别协议的概念。1

The book Site Reliability Engineering, and especially the “Service-Level Objectives” chapter, offers an in-depth discussion of quality attributes, introducing the concepts of service-level indicators, service-level objectives, and service-level agreements.1

1.拜尔、贝特西、克里斯·琼斯、詹妮弗·佩托夫和尼尔·墨菲。站点可靠性工程。波士顿:奥莱利,2016。

1.Beyer, Betsy, Chris Jones, Jennifer Petoff, and Niall Murphy. Site Reliability Engineering. Boston: O’Reilly, 2016.

质量属性可能会附带一些关于如何解释它们的内部指导,如下例所示:

Quality attributes could come with some internal guidance on how to interpret the them, as in this example:

过分的质量并不是质量

Over-quality is NOT quality

设计为约 10 倍增长,但计划在约 100 倍之前重写2

Design for ~10X growth, but plan to rewrite before ~100X2

2. Jeff Dean,“构建大规模信息检索系统的挑战”,Google, http://static.googleusercontent.com/media/research.google.com/en//people/jeff/WSDM09-keynote.pdf

2.Jeff Dean, “Challenges in Building Large-Scale Information Retrieval Systems,” Google, http://static.googleusercontent.com/media/research.google.com/en//people/jeff/WSDM09-keynote.pdf

然后,这些质量属性可以转化为针对系统的可执行场景,并且您可以用简单的英语句子表达质量属性(请参阅本章后面的“测试驱动架构”部分)。

These quality attributes can then be turned into executable scenarios against the system, and you can express the quality attributes in plain English sentences (see the section “Test-Driven Architecture,” later in this chapter).

权益驱动架构文档

Stake-Driven Architecture Documentation

关于建筑有很多观点。一些开发人员认为架构是关于大型系统的,包括基础设施、昂贵的中间件、分布式组件和数据库复制。对于不同的人来说这是正常的人们在不同的系统上工作,专注于软件的不同方面,并将其称为架构:他们可能使用术语架构来表示在其上下文中最重要的软件方面。

There are many perspectives on architecture. Some developers consider architecture as being all about the large-scale system, with its infrastructure, expensive middleware, distributed components, and database replication. It is normal for different people working on different systems to focus on different aspects of software and call it architecture: They may use the term architecture for the aspects of the software that are most at stake in their context.

在制作建筑型时,这种观点的多样性就变得很明显。在 Ted Neward 3提出的这种研讨会形式中,小组的任务是为给定的业务问题创建架构。每个小组有 30 分钟的时间和一张带有记号笔的大纸来准备和提出提案。规则明确强调小组成员应该能够证明所做的任何决定的合理性。研讨会结束时,每个小组向其他人展示其架构,就像在客户面前捍卫提案一样。其他与会者被邀请提出问题来质疑该提案,就像持怀疑态度的客户所做的那样。

This diversity of perspectives is made obvious when doing an architectural kata. In this workshop format, proposed by Ted Neward3, groups of people are tasked with creating an architecture for a given business problem. Each group has 30 minutes and a big piece of paper with markers to prepare and present a proposal. The rules clearly emphasize that the group members should be able to justify any decision made. The workshop ends with each group presenting its architecture to everyone else, as if defending the proposal in front of a client. Other attendees are invited to ask questions to challenge the proposal, as a skeptical client would do.

3. https://archkatas.herokuapp.com

3.https://archkatas.herokuapp.com

这样的研讨会提供了一种非常有趣的思考建筑的方式。它本身就是一种沟通练习。这不仅涉及做出的决定,还涉及以令人信服的方式表达这些决定。总是,一个型揭示了人们对同一问题的看法有多么不同。

Such a workshop provides a very interesting way to think about architecture. It is in itself a communication exercise. It is not just about the decisions made but also about expressing them in a convincing way. Invariably, a kata reveals how very differently people think about the same problem.

警告

Caution

您可能会想在实际业务案例中使用型方法,作为竞争性工程的一种形式,不同的小组提出不同的观点,然后进行比较。然而,风险在于,在真实案例中,最终会出现“赢家”和“输家”群体。你应该首先将型的想法作为纯粹的型练习几次,没有真正的赌注。你会从中获得很多价值和思考,你还将学习如何避免“赢家与输家”效应。

You might be tempted to use the kata method on real business cases, as a form of competitive engineering, with different groups proposing different views that are later compared. However, the risk is that on a real case, you would have “winner” and “loser” groups at the end. You should practice the kata idea several times as pure katas first, without real stakes. You will get a lot of value and thinking out of it, and you will also learn how to avoid the “winner versus loser” effect.

我从 katas 学到的是,不同的业务问题需要关注不同的领域。街头热狗摊贩的销售点系统的主要方面是重量轻、成本低(以防被盗)以及易于在中间快速制作热狗时使用。很少的人群。相比之下,要在应用程序商店上销售自己的移动应用程序必须首先具有视觉吸引力。再举一个例子,一个旨在每秒处理数百万个事务的企业系统首先应该关注性能作为其主要利益。此外,对于某些系统来说,主要的重点是对业务领域的更深入的了解。

What I have learned from katas is that different business problems call for focusing on different areas. The main aspects of a point-of-sale system for a hot dog vendor in the street are to be lightweight, low cost (in case it is stolen), and easy to use while making hot dogs in a hurry in the middle of a little crowd. In contrast, a mobile app meant to sell itself on an app store has to be primarily visually attractive. As yet another example, an enterprise system meant to serve millions of transactions per second should above all focus on performance as its main stake. In addition, with some systems the main stake is deeper understanding of the business domain.

该系统的关键在于要记录供每个人都知道的主要信息。例如,当整个项目的主要重点在于用户体验时,您不会想花费太多时间来记录服务器技术堆栈。

The key stakes of the system are the primary information to record for everyone to know. You wouldn’t, for example, want to spend too much of your time documenting the server technology stack when the main stake of the whole project is on the UX.

因此:尽早确定项目的主要利害关系,例如业务领域挑战、技术问题、用户体验质量或与其他系统的集成。你可能会回答这个问题:“什么最容易导致项目失败?” 确保您的文档工作主要涵盖主要内容。

Therefore: Identify early the main stake of the project, such as business domain challenge, technical concern, quality of the user experience, or integration with other systems. You may answer the question, “What would most easily make the project a failure?” Make sure your documentation efforts primarily cover the main stake.

明确的假设

Explicit Assumptions

当知识不完整时(通常是在任何有趣的项目开始时),我们都会做出假设。假设使得继续前进成为可能,但代价是以后可能会被证明是不正确的。当你重新考虑一个假设时,文档可以让你更便宜地倒带。创建此类文档的一个简单方法是使用决策所依赖的假设来明确标记决策。这样,当重新考虑某个假设时,就可以找到其所有后果,以便您可以依次重新考虑它们。为了使其有效地工作,所有这些都应该作为内部文档完成,放在决策中(通常在源代码本身中)。

When knowledge is incomplete, as it usually is at the beginning of any interesting project, we make assumptions. Assumptions make it possible to move on, but at the expense of potentially being shown to be incorrect later on. Documentation makes it cheaper to rewind the tape when you reconsider an assumption. A simple way to create such documentation is to explicitly mark decisions with the assumptions they depend upon. This way, when an assumption is reconsidered, it is possible to find all its consequences so you can reconsider them in turn. For this to work efficiently, it should all be done as internal documentation, in place within the decisions (usually in the source code itself).

简洁体现品质

Brevity Suggests Quality

一个好的架构是简单的并且看起来很明显。也很容易用几句话来描述。一个好的架构是一些关键的决策,这些决策是敏锐的、固执己见的,它们指导着其他所有的决策。

A good architecture is simple and looks obvious. It is also easy to describe in just a few sentences. A good architecture is a few key decisions, sharp and opinionated, that guide every other decision.

如果架构是“每个人都应该知道的”,那么这就为其复杂性设定了上限。任何难以解释的事情大多数人都无法理解。

If architecture is “what everyone should know,” then this puts an upper bound on its complexity. Anything that is complex to explain will not be understood by most.

提示

Tip

我在 Fred George 在 Øredev 2013 上关于微服务架构的演讲中看到了一个很好的架构示例。Fred 设法在几分钟内解释了该架构的关键思想。听起来好像是被简化了,而且很可能是故意的。每个人都可以快速理解的简化架构具有很多价值。如果优化每个细节会使整体无法快速解释,那么它就是有害的。

I saw a good example of a good architecture in Fred George’s talk at Øredev 2013 on microservices architecture. Fred manages to explain the key ideas of this architecture in minutes. It sounds as if it was simplified, and it probably is— deliberately. There is a lot of value in a simplified architecture that can be quickly understood by everyone. Optimizing every detail is harmful if it makes the whole impossible to explain quickly.

因此:尝试在两分钟以内大声表达一个架构,作为对其质量的检验。如果你成功了,请立即写下来。如果需要更长、太多的句子来解释架构,那么它可以改进很多。当然,架构可能太复杂,两分钟内无法详细解释。但这个测试挑战了高层结构的存在。架构不应该只是细节清单。

Therefore: Try to express an architecture out loud in less than two minutes as a test of its quality. If you succeed, write it down immediately. If it takes much longer and too many sentences to explain the architecture, then it can be improved a lot. Of course, an architecture may be too complicated to be explained in detail in two minutes. But this test challenges the presence of a high-level structure. An architecture should be more than just an inventory of details.

不断发展:变化友好的文档

Evolving Continuously: Change-Friendly Documentation

最好的架构是一个不断发展的生物,因为第一次尝试很难做到正确,然后它必须适应不断变化的环境。

The best architecture is an evolving creature, since it is hard to get it right on the first try, and then it has to adapt to the changing context.

好的架构易于简洁地解释,并最大限度地减少难以更改的决策数量。任何难以改变或每个人都应该知道的事情都必须记录下来。根据定义,它必须随着时间的推移而持久,并且可供每个人使用。

A good architecture is easy to explain succinctly and minimizes the number of decisions that are hard to change. Anything that is hard to change or that everybody should know has to be documented. It has to be persistent over time and made accessible to everyone, by definition.

这意味着必须避免任何使架构或其文档难以更改或更改成本高昂的事情。您的团队应该学习如何做出可逆决策或推迟不可逆决策。如果你害怕改变架构,因为你有很多关于它的静态文档必须重做,那么你的文档正在伤害你,你应该重新考虑如何做。

This means that anything that makes an architecture or its documentation hard or expensive to change must be avoided. Your team should learn how to make reversible decisions or to defer irreversible decisions. And if you fear changing the architecture because you have a lot of static documentation about it that would have to be redone, your documentation is harming you, and you should reconsider how you do it.

注意需要多少文字和图表来解释架构;越少越好。保持一切不断发展,并消除任何阻碍持续变化的流程或工件。

Pay attention to how many words and diagrams are needed to explain the architecture; the fewer being the better. Keep it all evolving, and remove any process or artifact that would impede continuous change.

决策日志

Decision Logs

为什么该项目要使用这种特殊的重量级技术?希望它被选择是因为一些要求,经过一些评估。谁还记得那件事?现在工作发生了变化,你能换个更简单的工作吗?

Why does the project use this particular heavyweight technology? Hopefully it was chosen because of some requirements, following some evaluation. Who remembers that? Now that the work has changed, could you switch to something simpler?

您在与利益相关者会面时谈论什么?从启动会议到冲刺计划会议和其他临时会议,涵盖了很多概念、思考和决策。所有这些知识会发生什么?有时它只存在于与会者的脑海中。有时,它会很快写成会议纪要并通过电子邮件发送。有时会拍摄并共享白板快照。有些人将所有内容都放入跟踪工具或他们的维基中。一个常见的问题是这些知识的组织方式往往缺乏结构。

What do you talk about during meetings with the stakeholders? From inception meetings to sprint planning meetings and other impromptu meetings, a lot of concepts, thinking, and decisions are covered. What happens to all this knowledge? Sometimes it only survives in the minds of the attendees. Sometimes it is quickly written as minutes of the meeting and sent by email. Sometimes a snapshot of the whiteboard is taken and shared. Some put everything into the tracker tool or in their wiki. One common problem is that this knowledge often lacks structure in the way it is organized.

因此: 维护最重要的架构决策的决策日志。它可以像代码存储库根目录下的文件夹中的结构化文本文件一样简单。使用代码库保持决策日志的版本。对于每个重要的决定,记录该决定、其理由(为什么做出)、考虑的主要替代方案以及主要后果(如果有)。切勿更新决策日志中的条目;相反,添加一个新条目来取代前一个条目并提供对其的引用。

Therefore: Maintain a decision log of the most important architectural decisions. It can be as simple as structured text files in a folder at the root of the code repository. Keep the decision log versioned with the code base. For each important decision, record the decision, its rationale (why it was made), the main alternatives considered, and the main consequences, if any. Never update the entries in the decision log; instead, add a new entry that supersedes the previous one and provide a reference to it.

Michael Nygard 将这样的决策日志称为架构决策记录,简称 ADR。4 Nat Pryce 创建了 adr-tools 以支持命令行的 ADR。5

Michael Nygard calls such a decision log an architectural decision record, or ADR for short.4 Nat Pryce created adr-tools to support ADRs from the command line.5

4. Michael Nygard,Think Relevance 博客, http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions

4.Michael Nygard, Think Relevance blog, http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions

5.纳特·普赖斯, https://github.com/npryce/adr-tools

5.Nat Pryce, https://github.com/npryce/adr-tools

形成解决方案的结构化假设是决策日志的一部分,是重要决策的基本原理的一部分。例如,如果您假设过去 24 小时内发布的文章占您网站访问量的 80% 以上,那么它将在决定将最近新闻和存档新闻划分为两个不同的子系统(每个子系统都有一个子系统)的基本原理显示出来。不同的当地建筑。

The structuring assumptions that shape a solution are part of the decision log, as part of the rationale for an important decision. For example, if you assume that articles published in the past 24 hours represent over 80% of the visits on your website, then it will show in the rationale for the decision to partition recent news and archived news as two distinct subsystems, each with a different local architecture.

在实践中,记录主要架构决策的基本原理并不总是那么容易,特别是当决策是出于错误的原因时(参见图 12.3)。例如,管理层可能坚持要包含这项技术,或者开发人员可能出于简历驱动的开发原因而坚持尝试这个新库。这样的道理很难写出来让大家都看得清楚!

In practice, it’s not always easy to record the rationale for major architecture decisions, especially when the decisions are made for the wrong reasons (see Figure 12.3). For example, management might have insisted on including this technology or the developers might have insisted on trying this new library for resume-driven development reasons. It’s hard to make such rationales explicit in writing for everyone to see!

图中显示了两个卡通人物,其中一个问“为什么这个状态机到处都是”,另一个回答“管理层要求的”。

图 12.3 记录不太可靠理由的决策日志

Figure 12.3 Decision log recording a not-so-solid rationale

您可以在 ADR 的 Arachne 框架存储库中在线找到 ADR 的优秀示例。6

You can find good examples of ADRs online in the Arachne-framework repository of ADRs.6

6.Arachne -Framework/architecture, https://github.com/arachne-framework/architecture

6.Arachne-Framework/architecture, https://github.com/arachne-framework/architecture

结构化决策日志的示例

An Example of a Structured Decision Log

在此处所示的示例中,决策日志作为单个 Markdown 文件维护在新索赔管理存储库的根目录中,位于愿景声明以及业务领域和主要利益相关者的描述之后。

In the example shown here, the decision log is maintained as a single Markdown file at the root of the new Claim Management repository, after the vision statement and the descriptions of the business domain and the main stakeholders.

主要决定

The Main Decisions

为了改善整体用户体验,决定:

To improve the overall user experience, it has been decided:

一种用户体验方法,专注于美观且用户友好的屏幕,跨移动设备响应,无论背后的实际应用程序如何,它们之间保持一致,并且具有快速的感知响应时间。重点还在于确保只需几次点击和很少的页面导航即可有效完成常见任务。

A UX approach, with a focus on beautiful and user-friendly screens, responsive across mobile devices, consistent between them regardless of the actual application behind, and with fast perceived response times. The focus is also on making sure that common tasks can be fulfilled efficiently, with few clicks and few pages navigation.

现有遗留软件的环境使得很难实现上述愿景。这就是为什么该计划的很大一部分是通过尽可能地退役来改造遗产。为了降低本次退役的风险,已做出以下决定:

The context of the existing legacy software makes it hard to achieve the vision stated above. This is why a large part of the program is to revamp the legacy, by decommissioning it as much as possible. To mitigate the risks of this decommissioning, the following decisions have been made:

  • 渐进的方法,频繁的交付:没有大爆炸。新模块和旧模块将共存,并逐步迁移到新代码。

  • A progressive approach, with frequent delivery: no Big Bang. New modules and legacy modules will co-exist, with a progressive migration to new code.

  • 领域驱动的设计方法有助于以在业务领域级别有意义的方式划分遗留问题,更好地理解领域机制,并在业务规则发生变化时更容易发展。

  • A domain-driven design approach to help partition the legacy in a way which makes sense at the business domain level, to better understand the domain mechanisms, and to be easier to evolve when the business rules change.

另一个挑战是,许多业务规则在高级调整者的头脑中是默认的,需要正式化。最重要的是,由于索赔需要几个月的时间才能完成,这些规则可能会在待处理索赔的有效期内发生变化。因此,做出以下决定:

Another challenge is that many business rules are tacit in the mind of senior adjusters and need to be formalized. On top of that, with claims taking months to complete, these rules may change during the life of a pending claim. As a consequence, the following decision has been made:

  • 一种业务流程建模方法,可在一个地方将隐性域业务规则形式化,并且可以轻松审核和更改。

  • A business process modeling approach to formalize tacit domain business rules in one place which can be easily audited and changed.

结果

Consequences

风险

Risks

风险之一是所选方法缺乏专业知识。为了降低这种风险,外部专家参与了:

One risk is the lack of expertise in the selected approaches. To mitigate this risk, external experts have been involved:

  • 用户体验专家(来自内部用户体验中心)

  • UX experts (from the internal UX center)

  • DDD专家(来自Arolla)

  • DDD expert (from Arolla)

另一个风险来自遗留环境,特别是:

Another risk comes from the legacy context, in particular:

  • 测试成本:缺乏各种自动化测试使得每个版本都变得昂贵(手动测试)和/或危险(测试不足)

  • Cost of testing: The lack of automated tests of all kinds makes each release expensive (manual testing) and/or dangerous (not enough testing)

  • 用户感知的性能:遗留系统速度缓慢,这使得它不适合最终用户感知的预期响应时间。

  • User-perceived performance: The legacy system is slow, which makes it not suited for the expected response time perceived by end users.

为了降低测试成本,并且在遗留系统的所有更改期间不妨碍用户,测试自动化将是关键(单元测试、集成测试、非回归测试),以保护系统免受回归或缺陷的影响。

To reduce the cost of testing, and to not impede the users during all the changes in the legacy, test automation will be key (unit tests, integration tests, non-regression tests) in order to protect the system against regressions or defects.

在用户感知性能的问题上,设计必须找到解决方法来提高感知性能,即使背后的遗留代码可能仍然很慢。

On the issue of user-perceived performance, the design will have to find workarounds to improve the perceived performance even though the legacy code behind may remain slow.

技术决策

Technical Decisions

新的索赔管理作为单一事实来源,直至客户接受索赔

New Claim Management as Single Source of Truth Until the Claim Is Accepted by the Customer

于 01/12/2015 接受

Accepted on 01/12/2015

语境

Context

我们希望避免因数据权限不明确而产生混乱,这会消耗开发人员的时间来修复失败的协调。这要求对于给定的域数据,在任何时间点都只能存在真实来源(也称为黄金来源)。

We want avoid confusion arising from unclear authority of data, which consumes developer time to fix failing reconciliations. This requires that only source of truth (aka Golden Source) can exist at any point in time for a given piece of domain data.

决定

Decision

我们认为索赔管理是索赔开始时索赔的唯一真实来源(也称为黄金来源),直到客户接受索赔为止,此时将其推送到旧索赔主机。从推送的那一刻起,唯一的事实来源就是遗留索赔主机(LCM)。

We decide that Claim Management is the only source of truth (aka golden source) for claim on claim inception and until the claim is accepted by the customer, at which time it is pushed to the legacy claim mainframe. From the moment it is pushed, the only source of truth is the legacy claim mainframe (LCM).

结果

Consequences

考虑到遗留背景,不幸的是,在索赔的整个生命周期中,一段时间内有必要拥有不同的黄金来源。尽管如此,在索赔生命周期的任何时刻,权威数据显然都来自一个来源。应重新考虑这一点,尽可能转向单一恒定源。

Given the legacy background, it is unfortunately necessary for some time to have a different golden source across the life of a claim. Still, at any point in the life of the claim, the authoritative data are clearly in one single source. This should be re- considered to move to one constant single source whenever possible.

由于存在这种差异,在推送之前:创建或更新索赔的命令将发送到索赔管理,同时发送事件,特别是发送到 LCM 以同步 LCM 数据(传统索赔主机作为读取模型。推送后:远程调用 LCM 用于更新LCM 中的索赔,事件发送回索赔管理以进行同步(索赔管理作为读取模型)。

Because of that discrepancy, before the push: commands to create or update a claim are sent to Claim Management, with events sent around and in particular to LCM to sync the LCM data (Legacy claim mainframe as a Read Model). After the push: remote calls to LCM are used to update the claim in LCM, with events sent back to Claim Management to sync it (Claim Management as a Read Model).

请参阅 InfoQ 上的“CQRS、读取模型和持久性”( https://www.infoq.com/news/2015/10/cqrs-read-models-persistence )。

See “CQRS, Read Models and Persistence” on InfoQ (https://www.infoq.com/news/2015/10/cqrs-read-models-persistence).

CQRS 和事件溯源

CQRS and Event Sourcing

于 01/06/2015 接受

Accepted on 01/06/2015

语境

Context

在索赔调整领域,审计至关重要:我们需要能够准确地判断发生了什么。

In the claim adjustment domain, audit is paramount: We need to be able to tell what happened in an accurate fashion.

我们希望利用索赔管理模型的写入和读取操作之间的不对称性,特别是加快读取访问速度。

We want to exploit the asymmetry between write and read actions to the Claim Management models, in particular to speed up read accesses.

我们还希望通过更加面向任务来跟踪用户意图。

We also want to keep track of the user intents by being more task oriented.

决定

Decision

我们遵循 CQRS 方法与事件溯源相结合。

We follow the CQRS approach combined with EVENT SOURCING.

结果

Consequences

我们选择 AxonFramework 来利用其现成的接口、注释和已编写的样板代码来构建开发。

We chose AxonFramework to structure the developments with its ready-made interfaces, annotations, and boilerplate code already written.

价值第一

Value-First

于 01/06/2015 接受

Accepted on 01/06/2015

语境

Context

我们希望避免因可变性而产生的错误。

We want to avoid bugs that arise from mutability.

我们还希望减少 Java 中创建值对象所需的样板代码量。

We also want to reduce the amount of boilerplate code necessary in Java to create value objects.

决定

Decision

只要有可能,我们就青睐有价值的对象。它们是不可变的,具有有价值的构造函数。需要时,他们可能会与建筑商一起来。

We favor value objects whenever possible. They are immutable, with a valued constructor. They may come with a builder when needed.

结果

Consequences

我们选择 Lombok 框架来帮助生成值对象及其 Java 构建器的样板代码。

We chose Lombok framework to help generate the boilerplate code for value objects and their builders in Java.

期刊或博客是大脑垃圾场

Journals or Blogs as Brain Dumps

使用正式决策日志的另一种方法是通过讲述发生的事情、您学到的内容以及团队如何做出决策、权衡或特定实施细节的完整故事来抛开您的大脑。

An alternative to using a formal decision log is to dump your brain by telling the full story of what happened, what you learned, and how the team came up with a decision, a trade-off, or a particular implementation detail.

在《学徒模式:有抱负的软件工匠指南》一书中,Dave Hoover 和 Adewale Oshineye 提倡记录您所学到的知识并分享您所学到的知识。7由团队成员撰写的博客是对任何其他类型文档的很好补充。它更加个性化,并且讲述的故事比大多数文档更引人注目。它讲述了冒险的重要片段,以及参与其中的人们的感受。

In the book Apprenticeship Patterns: Guidance for the Aspiring Software Craftsman, Dave Hoover and Adewale Oshineye advocate recording what you learn and sharing what you learn.7 A blog written by team members is a nice complement to any other kind of documentation. It is more personal, and it tells stories that are more compelling than most documentation. It tells important bits of the adventure, and the feelings of the people who were part of it.

7.大卫·H·胡佛和阿德瓦勒·奥辛内。学徒模式:有抱负的软件工匠指南。加利福尼亚州塞巴斯托波尔:O'Reilly Media, Inc. 2009。

7.Hoover, David H., and Adewale Oshineye. Apprenticeship Patterns: Guidance for the Aspiring Software Craftsman. Sebastopol, CA: O’Reilly Media, Inc. 2009.

丹·诺斯 (@tastapod) 似乎同意这一点。当与 Liz Kheogh (@lunivore) 和 Jeff Sussna (@jeffsussna) 交谈时,他在 Twitter 上说了以下内容:

Dan North (@tastapod) seems to agree. He said the following on Twitter, when talking to Liz Kheogh (@lunivore) and Jeff Sussna (@jeffsussna):

我喜欢拥有产品和/或团队博客。记录决策和对话,记录历史。它还显示了决策是如何做出的,并让您看到随着时间的推移品味或学习的变化。

I like having a product and/or team blog. Journaling decisions and conversations as you have them documents history. It also shows how decisions got made, and lets you see changing tastes or learnings over time.

分形架构文档

Fractal Architecture Documentation

在处理大型系统时,您应该放弃到处使用单一统一架构的想法。一个系统由多个子系统组成,每个子系统都应该有自己的架构,以及它们如何相互关联的总体架构。

When dealing with a large system, you should give up on the idea of having one single uniform architecture everywhere. A system is made of several subsystems, and each should have its own architecture, plus the overall architecture of how they’re interrelated.

因此:将您的系统视为几个较小的子系统或“模块”。它们可能是物理单元,例如组件或服务,也可能只是编译时的逻辑模块。独立记录每个模块的架构,并将模块之间的总体架构描述为一个系统级架构。

Therefore: Consider your system as several smaller subsystems, or “modules.” They may be physical units, such as components or services, or just logical modules at compile time. Document the architecture independently for each module and describe the overarching architecture between the modules as one system-level architecture.

通常,您可以使用包命名约定、源代码中的注释和一些纯文本的组合,通过内部文档记录每个模块的体系结构。您可以使用更多常青文档以纯文本形式记录整个架构,如果您有合适的文档,也许还可以使用一些特定的 DSL。然而,整体架构的文档也可以使用一些通过整合从每个模块提取的知识而构建的生成文档。

Typically, you document the architecture of each module with internal documentation, using a combination of package naming conventions, annotations in the source code, and a little plain text. You would document the overall architecture with more evergreen documents in plain text and perhaps some specific DSL if you have one that fits. However, the documentation of the overall architecture could also use some generated documents built by consolidation of the knowledge extracted from each module.

建筑景观

The Architecture Landscape

您的架构值得的不仅仅是一堆随机的图表和其他文档机制;所有这些努力都可以组织成一个整体,我们可以称之为架构景观,从 Andreas Rüping在他的书《敏捷文档:为软件项目生成轻量级文档的模式指南》中所说的文档景观中汲取灵感。在这本书中,安德烈亚斯建议将文档组织成“团队成员在检索或添加信息时用作心理地图的景观”。8这个想法是,文档的结构可以帮助用户导航它们,并且本身也可以添加知识。就活文档而言,问题是想象一个链接文档和图表(无论是否生成)的总体结构。

Your architecture deserves more than just a random bunch of diagrams and other documentation mechanisms; all these efforts can be organized into a whole that we can call an architecture landscape, drawing inspiration from what Andreas Rüping calls a document landscape in his book Agile Documentation: A Pattern Guide to Producing Lightweight Documents for Software Projects. In this book, Andreas suggests organizing the documents into a “landscape that team members use as a mental map when they retrieve or add information.”8 The idea is that the structure of the documents helps users navigate them and may also add knowledge in itself. In the case of living documentation, the problem is to imagine an overarching structure linking documents and diagrams, generated or not.

8.安德烈亚斯·茹平。敏捷文档:为软件项目生成轻量级文档的模式指南。英国奇切斯特:John Wiley & Sons, Ltd. 2003。

8.Rüping, Andreas. Agile Documentation: A Pattern Guide to Producing Lightweight Documents for Software Projects. Chichester, England: John Wiley & Sons, Ltd. 2003.

因此:随着您的文档逐渐包含多种文档机制,请将其组织成一个一致的整体,以便人们可以学习如何有效地导航。记录您的文档或遵守相关标准。

Therefore: As your documentation grows to include a number of documentation mechanisms, organize it into a consistent whole that people can learn to navigate efficiently. Document your documentation or conform to standards for that.

如果您碰巧喜欢的话,现成的架构文档模板可能会提供灵感:

Ready-made architecture document templates may provide inspiration, if you happen to like them:

  • 弧42

  • Arc42

  • IBM/Rational RUP

  • IBM/Rational RUP

  • 公司特定模板

  • Company-specific templates

一些模板尝试针对架构文档的每一个可能的需求进行规划。我讨厌费力地填写大模板。

Some templates try to plan for every possible bit of architectural documentation need. I loathe having to laboriously fill in large templates.

图片哈哈

LOL

我花了一周的时间编写一份软件架构文档,友好地称为 SAD。没有比这更合适的缩写词了。

I’ve spent one week working on a Software Architecture Document, friendly called SAD. No acronym would be more appropriate.

—推特上的@weppos

—@weppos on Twitter

模板作为清单最有用。例如,ARC 42“概念” 9部分是一个很好的清单,可以帮助您找出您可能忘记考虑的内容。以下是原始模板的缩写列表:

Templates are most useful as checklists. For example, the ARC 42 “Concepts”9 section is a nice checklist that can help you find out what you may have forgotten to consider. The following is an abbreviated list from the original template:

9. arc42, http://www.arc42.org

9.arc42, http://www.arc42.org

  • 人体工学

  • Ergonomics

  • 事务处理

  • Transaction Processing

  • 会话处理

  • Session Handling

  • 安全

  • Security

  • 安全

  • Safety

  • 沟通与整合

  • Communication and Integration

  • 合理性和有效性检查

  • Plausibility and Validity Checks

  • 异常/错误处理

  • Exception/Error Handling

  • 系统管理与行政

  • System Management and Administration

  • 记录、追踪

  • Logging, Tracing

  • 可配置性

  • Configurability

  • 并行化和线程化

  • Parallelization and Threading

  • 国际化

  • Internationalization

  • 移民

  • Migration

  • 缩放、集群

  • Scaling, Clustering

  • 高可用性

  • High Availability

在您当前的项目中,您忽略了哪些方面?其中有多少是您忽略记录的?

How many of these aspects do you neglect in your current project? How many of them do you neglect to document?

您可以从所有这些已建立的形式中汲取灵感,以逐个模块的方式导出您自己的文档环境。正如前面的“权益驱动架构文档”部分中所见,每个文档环境都集中于对该子系统的权益最重要的内容。

You can draw inspiration from all these established formalisms to derive your own documentation landscape, on a module-by-module basis. As seen previously in the section, “Stake-Driven Architecture Documentation,” focus each documentation landscape on what matters most for the stakes of this subsystem.

在具有丰富业务领域的模块上,您将主要关注领域模型及其作为关键场景的行为。在更多 CRUD 风格的模块上,可能没什么可说的,因为一切都是标准且显而易见的。在遗留系统上,可测试性和迁移可能是最具挑战性的方面,并且值得记录。

On a module with a rich business domain, you would focus primarily on the domain model and its behaviors as key scenarios. On a more CRUD-ish module, there might be very little to say, as everything is standard and obvious. On a legacy system, the testability and migration may be the most challenging aspects and would deserve the documentation.

您的文档环境可能是带有预定义项目符号和表格的纯文本文件,也可以采用小型注释库的形式,直接标记源代码元素及其架构贡献和基本原理。它可能是特定的 DSL。在实践中,您可以根据最有效的方式混合所有这些想法。您甚至可以使用 wiki 或专有工具来立即解决您的所有问题。

Your documentation landscape might be a plain-text file with predefined bullets and tables, or it can take the form of a small library of annotations, directly marking the source code elements with their architectural contributions and rationales. It could be a specific DSL. In practice, you would mix all these ideas according to what works best. You might even use a wiki or proprietary tools that might instantly solve all your problems.

系统的典型文档环境必须至少描述以下几点:

A typical documentation landscape for a system would have to at least describe the following points:

  • 系统的总体目的、背景、用户和利益相关者

  • The overall purpose of the system, its context, users, and stakeholders

  • 总体要求的质量属性

  • The overall required quality attributes

  • 关键业务行为和业务规则及业务词汇

  • The key business behaviors and business rules and business vocabulary

  • 总体原则、架构、技术风格以及任何固执己见的决定

  • The overall principles, architecture, technical style, and any opinionated decision

这并不意味着您需要创建包含所有这些信息的文档。活文档就是减少对手动编写文档的需求,并使用更便宜且保持最新的替代方案。

This does not mean at all that you need to create documents with all this information. Living documentation is all about reducing the need for manually written documents and using alternatives that are cheaper and remain up-to-date.

例如,您可以对第一点使用纯文本常绿文档,对第二点使用系统级验收测试,对第三点使用自动化的 BDD 方法,并在其中混合使用 README、codex 和自定义注释。最后一点的源代码。

For example, you could use plain-text evergreen documents for the first point, system-level acceptance tests for the second point, a BDD approach with automation for the third point, and a mix of a README, a codex, and custom annotations in the source code for the final point.

架构图和符号

Architecture Diagrams and Notations

长期以来,许多作者提出了形式主义来描述软件架构。有许多标准可供使用,例如 IEEE 1471“软件密集型系统架构描述的推荐做法”和 ISO/IEC/IEEE 42010“系统和软件工程架构描述”。克鲁赫滕的“4+1模式”得到了企业界的认可。然而,所有这些方法都不是轻量级的,并且需要一些学习曲线才能理解。每个都提供一组视图来描述软件系统的不同方面,包括逻辑视图、物理视图等。总体而言,这些方法在开发人员中并不是特别受欢迎。

Many authors have for a long time proposed formalisms to describe software architecture. A number of standards are available, such as IEEE 1471, “Recommended Practice for Architecture Description of Software-Intensive Systems,” and ISO/IEC/IEEE 42010, “Systems and Software Engineering Architecture Description.” Kruchten’s “4+1 model” has gained recognition in the enterprise world. However, all these approaches are not precisely lightweight, and they require some learning curve to be understood. Each provides a set of views to describe different aspects of the software system, with a logical view, a physical view, and so on. Overall, these approaches are not particularly popular among developers.

Simon Brown 承认需要一种轻量级替代方案,因此提出了 C4 模型,10一种轻量级的架构图方法,在开发人员中越来越受欢迎。这种方法吸引了特别是来自 Nick Rozanski 和 Eoin Woods 在他们的著作《软件系统架构11中的工作,并且具有无需事先培训即可使用的优点。它建议使用四种简单类型的图表来描述软件架构:

Simon Brown acknowledged the need for a lightweight alternative and consequently proposed the C4 Model,10 a lightweight approach to architecture diagrams that is becoming increasingly popular among developers. This approach draws in particular from the work of Nick Rozanski and Eoin Woods, in their book Software Systems Architecture,11 and has the benefit of being usable without prior training. It suggests four simple types of diagrams to describe a software architecture:

10. Simon Brown,编码架构博客, http://www.codingthearchitecture.com/2014/08/24/c4_model_poster.html

10.Simon Brown, Coding the Architecture blog, http://www.codingthearchitecture.com/2014/08/24/c4_model_poster.html

11. Rozanski、Nick 和 Eoin Woods,软件系统架构。波士顿:培生教育公司,2012 年。

11.Rozanski, Nick, and Eoin Woods, Software Systems Architecture. Boston: Pearson Education, Inc., 2012.

  • 系统上下文图:绘制和记录软件系统的起点,使您可以退一步查看全局

  • System context diagram: A starting point for diagramming and documenting a software system, which allows you to step back and look at the big picture

  • 容器图:说明高级技术选择,显示 Web 应用程序、桌面应用程序、移动应用程序、数据库和文件系统

  • Container diagram: To illustrate the high-level technology choices, showing web applications, desktop applications, mobile apps, databases, and file systems

  • 组件图:一种放大容器的方法,通过以对您有意义的方式进行分解(服务、子系统、层、工作流等)

  • Components diagram: A way to zoom into a container, by decomposing in a way that makes sense to you (services, subsystems, layers, workflows, and so on)

  • 类图:(可选)用一个或多个 UML 类图来说明任何特定的实现细节

  • Classes diagrams: (Optional) To illustrate any specific implementation detail with one or more UML class diagrams

我最喜欢的是系统上下文图,它既简单又明显,但经常被忽视。

My favorite is the system context diagram, which is both simple and obvious but is often neglected.

我认为这些通用符号永远不够。强烈的建筑风格应该用自己特定的视觉符号来表达。因此,虽然学习标准符号显然是一件好事,但您不应该将自己局限于它们,而是应该随意探索您自己的或更具体的替代方案(如果它们更具表现力)。

I think that these generic notations will never be enough. Strong architectural styles should be expressed with their own specific visual notation. So while it’s obviously a good thing to learn the standard notations, you shouldn’t limit yourself to them but should feel free to explore your own or more specific alternatives if they are more expressive.

建筑法典

An Architecture Codex

在向人们描述解决方案时,最关键的部分是分享导致解决方案的思考和推理。

When describing a solution to people, the most critical part is to share the thinking and reasoning that led to the solution.

Rebecca Wirfs-Brock 参加了 2012 年在布加勒斯特举行的 ITAKE 非会议,在她的演讲以及后来我们就此进行的对话中,她给出了 EcmaScript 的示例,其中清晰地记录了思维过程。她提到以下几点作为 ECMAScript 决策的一些理由:

Rebecca Wirfs-Brock was at the ITAKE un-conference in Bucharest in 2012, and during her talk and the later conversations we had about it, she gave the example of EcmaScript, where the thinking process is clearly documented. She mentioned the following as some of the rationales for decisions in ECMAScript:

  • 与其他现有民间传说有相似之处

  • Invoke similarities with other existing folklore

  • 通常我们希望尽可能少地学习和理解来完成工作

  • Usually we want to learn and understand as little as possible to do the job

  • 做出改变的秘诀:弄清楚以前是如何进行类似的改变的

  • Recipe for making change: Figure out how similar change has been done before

后来我在一家银行从事部门范围的架构工作,我引入了指导所有架构敏感决策的原则法典的想法(见图12.4)。该法典是根据具体决策案例的积累而建立的,试图正式阐明决策背后的推理。通常,这个原则已经存在于其他高级建筑师的头脑中,但它是心照不宣的,没有其他人知道。

Later I was working on a departmentwide architecture in a bank, and I introduced this idea of a codex of principles guiding all the architecture-sensitive decisions (see Figure 12.4). The codex was built from the accumulation of concrete cases of decision making by trying to elucidate formally the reasoning behind the decision. Often, the principle was already in the heads of other senior architects, but it was tacit, and nobody else knew about it.

代表全能法典的人物在讲台上展示了​​一本书。

图12.4 全能法典

Figure 12.4 The all-mighty codex

其中一些原则如下:

Some of these principles were as follows:

  • 了解你的黄金来源(即单一事实来源)。

  • Know your golden source (that is, single source of truth).

  • 不要喂怪物;改进遗产只会让它持续得更久。

  • Don’t feed the monster; improving the legacy only helps it last longer.

  • 提高直通式加工自动化比例。

  • Increase the straight-through processing automation ratio.

  • 客户的便利应该放在第一位。

  • Customer convenience should come first.

  • API 应该是第一位的!

  • The API should come first!

  • 手动流程只是电子流程的一个特例。

  • A manual process is just a special case of an electronic process.

事实证明,该法典对于参与该架构的每个人都很有用。我们的目标是向所有人发布该法典,即使它不完整且并不总是易于理解。至少它对于引发问题和反应是有用的。它从未正式发布,但法典的内容多次泄露,并多次被使用,以做出更一致的决策。

This codex proved useful for everybody involved in the architecture. The goal was to publish the codex for everyone, even if it was incomplete and not always easy to understand. At least it was useful for provoking questions and reactions. It was never formally published, but the content of the codex leaked on many occasions and has been used several times for more consistent decision making.

在最近的一次咨询工作中,我发现将团队的价值参考表达为偏好列表很有帮助,包括以下内容:

On a recent consulting gig I found it helpful to express the value referential of the team as a list of preferences, including the following:

  • 基于 XML 的代码。

  • Code over XML.

  • 模板引擎还可以,但不要考虑逻辑。

  • The templating engine is okay, but keep the logic out.

当然,采用文献中已有的标准原则也是一个好主意,因为它们提供了现成的文档。例如:

Of course, it is a good idea to adopt standard principles already that are documented in the literature, too, as they provide ready-made documentation. For example:

  • “让你的中间件保持愚蠢,让端点保持智能。” 12

  • “Keep your middleware dumb and keep the smarts in the endpoints.”12

12.纽曼,山姆。2015.构建微服务。加利福尼亚州塞瓦斯托波尔:O'Reilly Media, Inc.,2015。

12.Newman, Sam. 2015. Building Microservices. Sebastopol, CA: O’Reilly Media, Inc., 2015.

该法典解决了向更多人传播架构推理知识的需求,而不仅仅是架构团队,这是您应该关心的问题。

This codex addresses the need to spread the knowledge about architectural reasoning across more people than just the architecture team, which is a concern you should have.

因此:开始关注决策是如何制定的,并将隐含的原则、规则和启发法明确地纳入法典中。它可以像单个句子的项目符号列表一样简单。保持简短且易于大多数人掌握,例如在每个项目旁边使用一个简短的具体示例。只要有机会,就与人们分享此法典。您不必获得正式批准即可使用它。不断地改变它以保持简短和相关。

Therefore: Start paying attention to how decisions are made and make the tacit principles, rules, and heuristics underneath explicit into a codex. It can be as simple as a bulleted list of single sentences. Keep it short and easy to grasp for most people, such as by using a short concrete example next to each item. Share this codex with people whenever the opportunity arises. You don’t have to get it formally approved to be useful. Change it continuously to keep it short and relevant.

让法典成为一份永远不会完成的工作文件是非常重要的。每当你在其原则中遇到矛盾时,就该解决它或改进它。这不应被视为失败,而应被视为集体决策更加重要的机会。架构涉及共识,不是吗?

It is very important to keep a codex a working document that is never finished. Whenever you hit a contradiction in its principles, it’s time to fix it or evolve it. This should not be seen as a failure but as an opportunity for collective decision making to be even more relevant. Architecture involves consensus, doesn’t it?

架构代码可以是源代码管理中的文本文件、一组幻灯片,甚至可以用代码表示。以下是使用简单枚举来具体化法典原则的示例:

An architecture codex can be a text file in the source control, a set of slides, or even expressed in code. The following is an example of using a simple enum to materialize the principles of a codex:

1   /** 
2   * 团队同意的所有原则的列表。
3   */
4 公共枚举法典 {
5
6      /** 我们没有线索来解释这个决定 */
7 NO_CLUE("没有人"),
8
9      /** 权威的地方必须只有一个
      * 对于每条数据。*/
10 SINGLE_GOLDEN_SOURCE("团队"),
11
12      /** 让你的中间件保持愚蠢并保持智能
       * 在端点中。*/
13 DUMP_MIDDLEWARE("萨姆·纽曼");
14
15 私人最终字符串作者;
16
17 私人 Codex(字符串作者){
18 this.author = 作者;
19}
20}
1  /**
2  * The list of  all principles  the  team  agrees on.
3  */
4  public enum Codex {
5
6     /** We have no clue to explain this decision */
7     NO_CLUE("Nobody"),
8
9     /** There must be only one authoritative place
      * for each piece of data. */
10    SINGLE_GOLDEN_SOURCE("Team"),
11
12     /** Keep your middleware dumb and keep the smarts
       * in the endpoints. */
13     DUMP_MIDDLEWARE("Sam  Newman");
14
15     private  final  String author;
16
17     private Codex(String author) {
18         this.author  = author;
19     }
20 }

Sam Newman在他的《构建微服务》一书中说,他的同事 Evan Bottcher 在墙上制作了一张大海报,清晰地展示了关键原则,从左到右分为三栏:

In his book Building Microservices, Sam Newman says that his colleague Evan Bottcher created a big poster on the wall, displaying the key principles visibly, organized into three columns from left to right:

  • 战略目标(例如,实现可扩展的业务、支持进入新市场)

  • Strategic Goals (for example, enable scalable business, support entry into new markets)

  • 架构原则(例如,一致的接口和数据流,没有灵丹妙药)

  • Architectural Principles (for example, consistent interfaces and data flow, no silver bullet)

  • 设计和交付实践(例如,标准 REST/HTTP、封装遗留、COTS/SAAS 的最小化定制)

  • Design and Delivery Practices (for example, standard REST/HTTP, encapsulate legacy, minimal customization of COTS/SAAS)

这是一种将系统愿景、原则和实践总结在一个地方的好方法!

This is a nice way to sum up the system vision, principles, and practices in one place!

透明架构

Transparent Architecture

当架构文档嵌入到每个源代码存储库的软件工件中,并自动生成动态图表和活文档时,每个人都可以访问所有架构知识。相比之下,在一些公司中,架构知识仍然保留在只有官方架构师知道的工具和幻灯片中,并且没有保持最新状态。

When architecture documentation becomes embedded within software artifacts in each source code repository, with living diagrams and living documents generated out of them automatically, every individual has access to all the architecture knowledge. In contrast, in some companies the architecture knowledge remains in tools and slide decks only known by the official architects and not kept up-to-date.

将架构文档嵌入到软件工件中的结果之一是,它可以实现架构的分散化以及依赖于架构知识的决策。我将这种架构称为透明架构:如果每个人都可以自己看到架构的质量,那么每个人都可以自己做出相应的决策,而不必询问架构师角色的人(见图 12.5

One consequence of embedding architecture documentation within software artifacts is that it enables decentralization of the architecture and the decision-making dependent on architecture knowledge. I call this transparent architecture: If everyone can see the quality of the architecture by himself or herself, then everyone can make decisions accordingly, by themselves, without necessarily asking the people in an architect role (see Figure 12.5).

下图说明了如何在不同团队之间做出决策。

图12.5 通过了解全局,每个团队可以直接做出与整个系统一致的决策

Figure 12.5 With access to the whole picture, each team can directly make decisions that are consistent with respect to the whole system

例如,在微服务架构中,透明架构可以利用工作系统在运行时生成的活动系统图。这些知识已经存在于分布式跟踪基础设施(例如 Zipkin)中。您可能需要使用在仪器中添加的自定义注释和二进制注释来对其进行一些增强。

For example, in a microservices architecture, a transparent architecture can make use of living system diagrams generated out of the working system at runtime. This knowledge is already there in the distributed tracing infrastructure (for example, Zipkin). You may have to augment it a bit with custom annotations and binary annotations added in your instrumentation.

您不妨依赖您的服务注册表(例如,Consul、Eureka)及其标签来生成活文档。如果您应用这种做法,服务之间的依赖关系也可以从消费者驱动的合同中派生出来。如果您关心物理基础设施,则可以通过 Graphviz 通过编程 API 从云获取的数据生成自定义生活图表,使其可见。13请注意,更多“良性”实践也会使活文档变得更容易!

You might as well rely on your service registry (for example, Consul, Eureka) and its tags to produce living documents. Dependencies between services can also be derived from consumer-driven contracts if you apply this practice. And if you care about the physical infrastructure, it can be made visible through custom living diagrams generated with Graphviz from data you get from your cloud through its programmatic API.13 Note that more “virtuous” practices also make living documentation easier!

13. James Lewis 在他的一些演讲中展示了一个由 cron、Python、Boto、pydot 和 Graphviz 生成的云基础设施动态图示例。

13.James Lewis has been showing in some of his talks an example of a living diagram of a cloud infrastructure generated from cron, Python, Boto, pydot, and Graphviz.

您可以通过增强代码、架构文档中的注释、决策日志和架构强制指南来实现透明架构,它们一起可以释放架构现实检查的好处,如本章后面所述。

You can achieve transparent architecture through augmented code, annotation in architectural documentation, decision logs and architectural enforced guidelines, which together can unlock the benefits of an architectural reality check, as described later in this chapter.

架构注释

Architectural Annotations

任何可以使代码更加明确的设计信息都值得添加。如果遵循层模式,则可以通过在每层根部的@Layer包上使用自定义注释来记录代码:com.example.infrastructure/package-info.java

Any design information that can make the code more explicit is worth adding. If you follow the layers pattern, you can document code by using the custom annotation @Layer on the package com.example.infrastructure/package-info.java at the root of each layer:

1 @Layer(LayerType.INFRASTRUCTURE)
2 包com.example.infrastruct;
1  @Layer(LayerType.INFRASTRUCTURE)
2  package com.example.infrastructure;

类似刻板印象的模式表示限定语言元素(如方法)的内在角色或属性。考虑这个例子:

Stereotype-like patterns represent intrinsic roles or properties that qualify a language element like a method. Consider this example:

1 @幂等
2无效商店(Customer顾客);
1  @Idempotent
2  void  store(Customer customer);

另请考虑以下示例:

Also consider this example:

1 @SideEffect(SideEffect.WRITE, "数据库")
2 void 保存(事件 e){...}
1  @SideEffect(SideEffect.WRITE,  "Database”)
2  void  save(Event  e){...}

具体的风险或关注点也可以直接在相应的类、方法或字段上表示,如下所示:

Specific risks or concerns can also be denoted directly on the corresponding class, method, or field, as shown here:

1 @Sensitive(“欺诈风险”)
2 公共期末班信用卡{...
1  @Sensitive("Risk of Fraud")
2  public final class CreditCard {...

一般来说,设计模式是设计注释的良好候选者。您可以在主动参与模式的元素上放置注释。您可以通过考虑如果删除了模式是否应该保留该元素来检查它。如果没有,您可以安全地在其上声明模式;类或方法只是为了实现该模式。通常是角色中的元素具有模式本身的名称,例如适配器或命令。

Design patterns in general are good candidates for design annotations. You place an annotation on an element that participates actively in the pattern. You can check it by considering whether you should keep the element if you removed the pattern. If not, you can safely declare the pattern on it; the class or method is only there to realize the pattern. It is often the element in the role that has the name of the pattern itself, such as adapter or command.

有时您需要在注释中添加值。例如,如果您想声明操作特定聚合的 DDD 存储库模式的出现,您可以这样做:

Sometimes you need values in your annotations. For example, if you wanted to declare an occurrence of the DDD repository pattern that is manipulating a particular aggregate, you could do it like this:

1 @Repository(aggregateRoot = Customer.class)
2 公共接口 AllCustomers {...
1  @Repository(aggregateRoot  =  Customer.class)
2  public interface AllCustomers {...

您可以使用最常用的模式创建自己的模式目录。它可能包括来自 Gang of Four、DDD、Martin Fowler(分析模式和 PoEAA)、EIP、一些 PLoP 和 POSA 模式以及一些众所周知和/或琐碎的基本模式和习语,以及所有自定义的内部模式模式。

You can create your own patterns catalog with the patterns you use most commonly. It might include patterns from the Gang of Four, DDD, Martin Fowler (analysis patterns and PoEAA), EIP, some PLoP and POSA patterns, and several well-known and/or trivial basic patterns and idioms, plus all your custom in-house patterns.

此外,您可以创建自定义注释来对一些重要的知识源进行分类,例如业务规则、策略等,如下所示:

In addition, you might create custom annotations to classify some important sources of knowledge, such as business rules, policies, and so on, as shown here:

1 @BusinessRule
2 公共日期shiftDate(日期日期,BusinessCalendar日历)
{...}
1  @BusinessRule
2  Public Date shiftDate(Date date, BusinessCalendar calendar)
{...}

这里还有一些例子:

Here are some more examples:

  • @Policy突出软件中表达的主要公司政策

  • @Policy to highlight the major company policy expressed in the software

  • @BusinessConvention表示较低级别的策略,这些策略只是业务领域中的约定

  • @BusinessConvention to denote the lower-level policies that are just conventions in the business domain

  • @KeyConcept@Core强调重要的事情

  • @KeyConcept or @Core to emphasize what’s important

  • @Adapter@Composite表示模式的使用

  • @Adapter or @Composite to denote the use of a pattern

  • @Command@Query澄清模块上写入或读取的语义或

  • @Command or @Query to clarify the semantics of write or read on a module or

  • @CorrelationIDAggregateID在田野上

  • @CorrelationID or AggregateID on a field

强制设计决策

Enforced Design Decisions

由于使用设计知识(使用注释、命名约定、服务注册表中的标签或任何其他机制)增强了代码,您可以将一致性检查委托给工具。您可以根据声明的模式和构造型知识检查依赖关系。如果注释为值对象的类与注释为实体或服务的类具有字段级依赖关系,我喜欢引发异常。这就是我的品味,我经常要求工具帮我检查这些事情:

Thanks to the augmentation of code with design knowledge (using annotations, naming conventions, tags in your service registry, or any other mechanism), you can delegate conformity checks to tools. You can check dependencies according to the declared patterns and stereotypes knowledge. I like to raise an anomaly if a class annotated as a value object has field-level dependencies to classes annotated as entity or service. That’s my taste, and I often ask tools to check these things for me:

1 if (type.isInvolvedIn(VALUE_OBJECT)) {
2 if (dependency.isInvolvedIn(实体) ||
3 dependency.isInvolvedIn(SERVICE)) {
4 ...提出异常
5 }
1  if  (type.isInvolvedIn(VALUE_OBJECT))  {
2    if  (dependency.isInvolvedIn(ENTITY)  ||
3      dependency.isInvolvedIn(SERVICE))  {
4        ... raise an anomaly
5  }

您还可以在静态分析工具中创建自定义规则。例如,使用 SonarQube 内置架构约束模板或专门的架构断言库(例如 ArchUnit)14 ,您可以创建以下规则:

You can also create custom rules in your static analysis tool. For example, using the SonarQube built-in architectural constraint template or a specialized architectural assertion library such as ArchUnit,14 you could create these rules:

14.ArchUnit https://github.com/TNG/ArchUnit

14.ArchUnit, https://github.com/TNG/ArchUnit

  • “持久层不能依赖于Web代码”:禁止.web.**.dao.

  • “Persistence layer cannot depend on web code”: Forbid access to .web. from classes in **.dao.

  • “六边形架构”:.domain.禁止从到 的访问.infra

  • “Hexagonal architecture”: Forbid access from .domain. to .infra.

  • “值对象不应将服务作为成员字段注入”:由 注释的类ValueObjects不应具有由 注释的类型的字段DomainService

  • “Value objects should not have services injected as member fields”: Classes annotated by ValueObjects should not have fields of a type annotated by DomainService.

例如,以下内容强制执行第 8 章“可重构文档”中提到的命名准则:

For example, the following enforces a naming guideline mentioned in Chapter 8, “Refactorable Documentation”:

1 @测试
2 公共无效domain_classes_must_not_be_named_with_prefix(){
3 noClasses().that().resideInAPackage("..domain..")
4 .should().haveSimpleNameEndingWith("服务")
5 .check(导入的类);
6 noClasses().that().resideInAPackage("..domain..")
7 .should(new DomainClassNamingCondition())
8 .check(导入的类);}
1 @Test
2 public void domain_classes_must_not_be_named_with_prefix() {
3   noClasses().that().resideInAPackage("..domain..")
4    .should().haveSimpleNameEndingWith("Service")
5    .check(importedClasses);
6   noClasses().that().resideInAPackage("..domain..")
7    .should(new DomainClassNamingCondition())
8    .check(importedClasses);}

在本例中,DomainClassNamingCondition自定义代码用于检查名称是否不以此列表中的前缀结尾:Service, Repository, Value-Object, VO, Entity, Interface, Manager, Helper, DAO, DTO, Intf, Controler, 或Controller

In this case, DomainClassNamingCondition is custom code to check that the name doesn’t end with a prefix from this list: Service, Repository, Value-Object, VO, Entity, Interface, Manager, Helper, DAO, DTO, Intf, Controler, or Controller.

以下规则强制执行六边形架构约束“禁止从域代码访问基础设施代码”:

The following rule enforces the hexagonal architecture constraint “forbid access from domain code to infrastructure code”:

1 @测试
2 公共无效domain_must_not_depend_on_anything() {
3 noClasses().that().resideInAPackage("..domain..")
4.should().accessClassesThat()
5 .resideOutsideOfPackage("..域..")
6 .check(导入的类);
7}
1 @Test
2 public void domain_must_not_depend_on_anything() {
3   noClasses().that().resideInAPackage("..domain..")
4    .should().accessClassesThat()
5          .resideOutsideOfPackage("..domain..")
6    .check(importedClasses);
7 }

规则的名称及其声明性描述清楚地记录和保护设计决策——作为简单的源代码。

The name of the rules and its declarative description clearly document and protect the design decisions—as plain source code.

建筑现实检查

Architectural Reality Check

架构不应该被定义,而应该被发现、完善、发展和解释。#theFirstMisconceptionAboutArchitecture

Architecture should not be defined but discovered, refined, evolved, and explained. #theFirstMisconceptionAboutArchitecture

——推特上的@mittie

—@mittie on Twitter

老式的架构理念是在实施之前先执行的事情,这种想法不太适合现代项目。无论何时何地,代码和架构(无论您如何称呼架构)都会发生变化。

The old-fashioned idea of architecture as something to perform before doing the implementation doesn’t fit well with modern projects. Change is expected everywhere and at any time, in the code and in the architecture—whatever you call architecture.

您希望确保满足整个系统的主要质量属性(例如,概念完整性、性能、可维护性、安全性、容错性),并且将最重要的决策传达给所有相关人员。但您不希望老式的架构实践拖慢项目的速度。您需要快速的文档来帮助向每个人传达知识,并且还可以帮助推理并确保满足质量属性。

You want to ensure that the major quality attributes of the overall system are met (for example, conceptual integrity, performance, maintainability, security, fault tolerance) and that the most important decisions are being communicated to everyone involved. But you don’t want old-fashioned architecture practices to slow down the project. You want fast documentation that can help communicate knowledge to everyone and that can also help reason and make sure the quality attributes are satisfied.

但还有一个问题:该架构的具体实现可能与其意图不符。编码决策可能日复一日地发生变化,一次一个小错误,直到系统与它要实现的架构完全不同。这个问题称为架构侵蚀15

But there is another problem: The concrete implementation of the architecture may not match its intent. Coding decisions may drift day after day, one small mistake at a time, until the system bears no resemblance to the architecture it was meant to implement. This problem is called architecture erosion.15

15. Ricardo Terra、Marco Tulio Valente、Krzysztof Czarnecki 和 Roberto S. Bigonha,“推荐重构以扭转软件架构侵蚀”,第 16 届欧洲软件维护和再工程会议,2012 年:gsd.uwaterloo.ca/sites/default/files /Full%20Text.pdf

15.Ricardo Terra, Marco Tulio Valente, Krzysztof Czarnecki, and Roberto S. Bigonha, “Recommending Refactorings to Reverse Software Architecture Erosion,” 16th European Conference on Software Maintenance and Reengineering, 2012: gsd.uwaterloo.ca/sites/default/files/Full%20Text.pdf

请注意,质量属性要求通常不会经常更改,但代码中的决策会经常更改。

Note that the quality attributes requirements usually don’t change very frequently, but the decisions in the code do.

因此:随着软件的变化定期可视化架构。将实现的架构与预期的架构进行比较。如果它们不同,您可能需要调整其中之一。借助实时图表或其他实时文档的自动支持,可以在每次构建期间经常进行这种比较。

Therefore: Regularly visualize the architecture as the software changes. Compare the architecture as implemented to your architecture as intended. If they differ, you may want to adjust one or the other. With automated support of living diagrams or other living documents, this comparison can be done as often as during each build.

所有这些都假设您对预期的架构应该有一定的了解。但如果您没有,您可以从已实现的架构中逐步对其进行逆向工程。

All this assumes that you have some vision of what your intended architecture should be. But if you don’t have one, you can gradually reverse engineer it from your architecture as implemented.

有一些工具可以帮助架构可视化和检查,您还可以创建自己的生活图表生成器,完全致力于您自己的特定上下文。

There are tools available that can help with architecture visualization and checking, and you can also create your own living diagram generator totally dedicated to your own specific context.

测试驱动架构

Test-Driven Architecture

测试驱动开发的心态不仅仅是“小规模”地编写代码。这是一门纪律,首先要描述你想要什么,然后再实施它,然后将其清理干净,以从长远来看增强你的工作。

Test-driven development has a mindset that is not just for writing code “in the small.” It’s a discipline that involves first describing what you want, before you implement it, and then you make it clean to enhance your work in the longer term.

您可以尝试在架构规模上遵循相同的过程。你面临的挑战是规模更大反馈循环更长,这意味着当你最终得到反馈时,你可能会忘记你想要的是什么。

You can try to follow this same process at the architecture scale. The challenges you face are the larger scale of everything and the longer feedback loops, which means you may forget what you were after when you eventually get the feedback.

理想情况下,您可以首先将所需的质量属性定义为测试。它们不会在数周或数月内过去;当他们最终过去时,他们成为唯一对当前质量属性的真正真诚的记录。例如,考虑以下性能质量属性:

Ideally, you would start by defining the desired quality attributes as tests. They will not pass for weeks or months; when they eventually pass, they become the only really sincere documentation of the current quality attributes. For example, consider this performance quality attribute:

超过 500 万次的 10k 请求,错误率低于 0.1%,响应时间在 100 毫秒内(99.5 百分位数)

10k requests over 5mn with less than 0.1% error and response time within 100ms at 99.5 percentile

首先将其写在质量属性的项目符号列表中,例如 Markdown 文件中。然后尽可能在实际环境(甚至在生产环境中)上执行此标准,就像 Gatlin 或 JMeter 测试一样。它不太可能立即通过。现在,团队可以根据优先级来处理它以及其他事情。可能需要几次冲刺才能通过。

First write it down in a bulleted list of quality attributes, such as in a Markdown file. Then implement this criterion as literally as possible as a Gatlin or JMeter test on a realistic environment (even on production). It’s not very likely that it will pass right away. Now the team can work on it, along with other things, depending on the priorities. It may take a few sprints to make it pass.

如果您创建用于概念证明的测试脚本,您可能已经做了类似的事情。与其事后丢弃这些脚本,不如将您已经一次性进行的实验转变为可维护的资产,这些资产可以断言您仍然满足要求,并且可以同时记录它们时间。

You might already do something similar if you create test scripts for proofs of concepts. Instead of throwing away these scripts afterward, it doesn’t take much more effort to turn experiments you’re already doing on a one-off basis into maintainable assets that can assert that you still meet the requirements and that can document them at the same time.

质量属性作为场景

Quality Attributes as Scenarios

测试应尽可能以声明方式描述质量属性。实现此目的的一种方法是将标准设计为特殊的 Cucumber 场景:

A test should describe the quality attribute as declaratively as possible. One way to do this is to dress the criterion as a special Cucumber scenario:

@QualityAttribute @SlowTest @ProductionLikeEnv @Pending

@QualityAttribute @SlowTest @ProductionLikeEnv @Pending

场景:高峰期请求数

Scenario: Number of requests at peak time

鉴于系统部署在类似生产的环境中

Given the system is deployed on a production-like environment

当接收超过 500 万个 10k 请求的负载时,错误率小于 0.1%,响应时间在 99.5 百分位处低于 100ms

When it receives a load of 10k requests over 5mn, the error rate is less than 0.1%, and the response time is below 100ms at 99.5 percentile

请注意此处的自定义标签:

Note the custom tags here:

  • @QualityAttribute:将某物分类为质量属性要求

  • @QualityAttribute: To classify something as a quality attribute requirement

  • @SlowTest:仅作为夜间缓慢测试运行的一部分启动某些内容

  • @SlowTest: To launch something only as part of the nightly slow tests run

  • @ProductionLikeEnv:标记此测试仅在类似生产的环境中相关,以使指标有意义

  • @ProductionLikeEnv: To flag that this test is relevant only in a production-like environment for the metrics to be meaningful

  • @Pending:表明这种情况还没有过去

  • @Pending: To signal that this scenario is not passing yet

通过这种方法,一旦编写了场景,它就可以成为相应质量属性的单一事实来源。此外,场景测试报告也成为这些“非功能性需求”的目录。

With this approach, as soon as the scenario is written, it can become the single source of truth for the corresponding quality attribute. Moreover, the scenario tests reports becomes the table of contents for these “non-functional requirements,” too.

请注意,即使质量属性场景从未实际实现为真正的测试,它们也是有用的。

Note that the quality attributes scenarios are useful even if they are never actually implemented as true tests.

您可以这样描述质量属性:

You might describe the quality attributes this way:

  • 持久性: “假设已经写入了一次购买,当我们关闭并重新启动服务然后再进行购买时,我们可以读取所有购买数据。” 像这样记录显而易见的事情是否太过分了?

  • Persistence: “Given that a purchase has been written, when we shut down and then restart the service and then the purchase, we can read all the purchase data.” Is it going too far to document the obvious like this?

  • 安全性: “当我们运行标准渗透测试套件时,检测到零缺陷。” 请注意,这里的技巧是“标准”一词,它指的是场景之外某个地方的更完整的描述。此外部链接也是您文档的一部分,即使它不是您自己编写的。

  • Security: “When we run standard penetration testing suites, zero flaw is detected.” Note that here the trick is the word standard, which refers to a more complete description somewhere outside the scenario. This external link is part of your documentation, too, even if you didn’t write it yourself.

当可以在编译时检查质量属性时,它将成为质量仪表板的一部分(例如,在 Sonar 中)。在这种情况下,您可以将此工具转变为这些质量属性的目录。如果发生太多违规,您可以使用诸如 Build Breaker 插件之类的东西来使构建失败。这是实施强制准则的另一种方式。

When the quality attribute can be checked at compile time, it will be part of your quality dashboard (for example, in Sonar). In this case, you can turn this tool into your table of contents for these quality attributes. And you might use something like the Build Breaker plugin to fail the build in case of too many violations. This is another way of implementing enforced guidelines.

生产运行时的质量属性

Quality Attributes at Runtime in Production

有些质量属性很难在自然栖息地之外进行测试。这种情况需要采取更加以监控为导向的方法。Netflix 引入了 Chaos Monkey 来保证服务级别的容错能力。后来它在数据中心层面引入了 Chaos Gorilla:

Some quality attributes are too difficult to test outside their natural habitat. Such situations call for a more monitoring-oriented approach. Netflix introduced the Chaos Monkey to assert fault tolerance at the service level. Later it introduced the Chaos Gorilla at the data center level:

Chaos Gorilla 与 Chaos Monkey 类似,但模拟整个 Amazon 可用区的中断。我们希望验证我们的服务是否自动重新平衡到功能可用区域,而无需用户可见的影响或手动干预。16

Chaos Gorilla is similar to Chaos Monkey but simulates an outage of an entire Amazon availability zone. We want to verify that our services automatically re-balance to the functional availability zones without user-visible impact or manual intervention.16

16. Netflix 技术博客, http://techblog.netflix.com/2011/07/netflix-simian-army.html

16.The Netflix tech blog, http://techblog.netflix.com/2011/07/netflix-simian-army.html

对这两个混沌引擎的简单描述,以及它们在中断频率方面的配置参数,本身就是容错要求的文档。

The mere description of these two Chaos engines, along with their configuration parameters in terms of outage frequencies, is in itself a documentation of the fault-tolerance requirements.

如果部署后某些指标降级,某些云提供商或容器编排工具支持自动回滚。此配置实际上记录了被视为“正常”指标的内容(例如,CPU/内存使用情况、转换率)。

Some cloud providers or container orchestration tools support automatic rollback if some metrics are degraded following a deployment. This configuration de facto documents what’s considered “normal” metrics (for example, CPU/memory usage, conversion rate).

其他质量属性

Other Quality Attributes

在对产品进行实验之前跟踪您的期望、其成功和失败:http://growth.Founders.as #startup #hypotheses

To keep track of your expectations before doing experiments on the product, its successes and failures: http://growth.founders.as #startup #hypotheses

—推特上的@fchabanois

—@fchabanois on Twitter

某些质量属性无法自动测试(例如,财务预期、用户满意度)。这些属性通常驻留在共享驱动器上的电子表格中。网上存在替代方案,鼓励在将目标与实际成就进行比较之前真诚地声明目标。这些类型的工具鼓励以 TDD 式的方式工作以实现启动目标。

Some quality attributes cannot be tested automatically (for example, financials expectations, user satisfaction). These attributes often reside within spreadsheets on shared drives. Alternatives exist online to encourage sincere declarations of the objectives before comparing them against actual achievements. These kinds of tools encourage working in a TDD-ish fashion for startup objectives.

从零散的知识到可用的文档

From Fragmented Knowledge to Usable Documentation

本节中描述的方法最终可能会产生有关所有质量属性的许多零散且异构的事实来源。它们需要被整理并整合到一两个活生生的内容表中。

The approach described in this section can end up producing many fragmented and heterogenous sources of truths about all the quality attributes. They need to be curated and consolidated into one or two living tables of content.

因此:将质量属性测试打扮成 Cucumber 场景,并将它们放在单独的“质量属性”文件夹中(因此放入相应的实时文档中的单独章节中)。使用标签可以更准确地对它们进行分类。决定使用一种现有工具来托管主目录,作为所有质量属性文档的单一入口点,并引用任何其他工具。

Therefore: Dress your quality attribute tests as Cucumber scenarios and put them in a separate “Quality Attributes” folder (and hence into a separate chapter in the corresponding living documentation). Use tags to classify them more precisely. Decide on one existing tool to host the main table of content as the single point of entry for all the quality attributes documentation, with references to any other tool.

例如,您可能决定 Cucumber 是主目录。然后,您可以添加伪场景以链接到 Sonar 配置以及每个静态分析工具配置的永久链接。您还可以提及 Chaos Monkey 作为一个场景以及指向其在某些 Git 存储库上的配置的链接。

For example, you might decide that Cucumber is the main table of contents. You can then add pseudo-scenarios to link to the Sonar configuration and to the permalinks to the configuration of each static analysis tools. You might also mention the Chaos Monkey as a scenario and a link to its configuration on some Git repository.

或者,您可以决定将构建工具作为主要目录。通过在构建管道(例如 Jenkins、Visual Studio)中添加自定义步骤,您可以精确定位 Cucumber 报告、Sonar 报告和 Chaos Monkey 配置。

Alternatively, you might decide on your build tool as the main table of contents. By adding custom steps in the build pipeline (for example, Jenkins, Visual Studio), you can pinpoint to Cucumber reports, Sonar reports, and the Chaos Monkey configuration.

这些工具同时可以是目录,如果不再满足其中一个质量属性,则构建失败。这有助于保持文档的真实性。如果您只是使用 wiki 作为主要目录,那么您就不再具有这种强制力。

These tools can at the same time be a table of contents and fail the build in case one of the quality attribute is not met anymore. This helps keep the documentation sincere. If you just use a wiki as the main table of contents, you no longer have that enforcement.

作为活建筑文档的小规模模拟

Small-Scale Simulation as Living Architecture Documentation

大型且复杂的软件应用程序或应用程序系统在文档方面具有挑战性。从源代码的大小和它们的配置来看,描述它们所需的知识量是如此之大巨大,按原样毫无用处。与此同时,关键的高层设计决策和构建系统的所有想法通常都是隐含的。

Large and complex software applications or systems of applications are challenging in terms of documentation. Judging by the size of the source code and configuration they’re made of, the amount of knowledge that is necessary to describe them is so huge it is useless as is. And at the same time, the critical higher-level design decisions and all the thinking that went into building the systems are often implicit.

如果系统更小,就会更容易理解。只需阅读少数几个类,运行一些测试,探索在 REPL 中使用代码时会发生什么,并观察运行时的动态行为,您就可以快速充分了解其目的及其工作原理。即使主导设计的思想丢失了,您也可以通过观察正在运行的小规模系统来恢复它。这将是隐性知识,但仍然比没有好得多。

If a system were smaller, it would be easier to understand. Just by reading the handful of classes, running the few tests, exploring what happens when playing with the code in a REPL, and watching the dynamic behavior at runtime, you could quickly get a sound understanding of its purpose and of how it works. Even if the thinking that led the design were lost, you would be able to recover it from observing the small-scale system in action. This would be tacit knowledge, but it would still be much better than nothing.

因此:创建软件系统的小规模副本,例如仅出于文档记录的目的,通过一些测试仅对关键代码进行精简重新实现。通过积极的管理,选择一小部分功能和代码,仅关注重要的一个或两个方面,并且适合您的整个大脑。简化所有其他分散注意力的方面,即使这样做会降低速度并提供有限的功能。确保这个小规模的复制品能够真实地工作,产生准确的(即使不是完美的)结果,尽管它不一定需要在所有情况下都这样做。

Therefore: Create a small-scale replica of your software system, such as a stripped-down reimplementation of just the key bits of code with some tests, for the sole purpose of documentation. Through aggressive curation, select a small subset of features and code that focus only on the one or two aspects that matter and that fits on your brain as a whole. Simplify every other distracting aspect, even if doing so makes it slower and gives it a limited set of features. Make sure this small-scale replica works realistically, producing accurate, if not perfect, results, though it does not necessarily need to do so in all cases.

小规模模拟的优点是它变得人性化;它适合你的头脑。请注意,这里当我说小规模时,我主要是指降低复杂性,而不仅仅是减小尺寸。

The advantage of small-scale simulation is that it becomes human-scale; it fits in your head. Note that here when I say small-scale, I’m primarily talking about reduced complexity, not just reduced size.

我已经尝试过几次小规模模拟:

I’ve tried small-scale simulation several times:

  • 在开发金融产品的交易系统时,由于各种优化,撮合引擎的核心变得越来越大、越来越复杂,而其他问题,例如定时、调度和权限管理,使整体情况变得模糊。我们创建了匹配引擎核心的较小版本,仅包含一组最小的基本和朴素类,可以使匹配工作在最有趣的方面。在这种情况下,较小的系统不是复制品,而是主要由与实际系统相同的元素构建,因为设计足够灵活以适应这一点。

  • When developing an exchange system for financial products, the core of the matching engine was growing larger and more complex because of various optimizations, and other concerns, such as timing, scheduling, and permissions management, were blurring the overall picture. We created a smaller version of the core of the matching engine, with just a minimal set of basic and naive classes that could make matching work in its most interesting aspects. In this case, the smaller system was not a replica but was built mostly from the same elements as the actual system because the design was flexible enough to accommodate that.

  • 在一个非常大的遗留系统中,有几个应用程序和每天多次在后台运行的许多批处理,系统的整体行为非常模糊。我们为最重要的批次创建了一个小规模、简化的 Java 模拟,以便我们可以更好地理解它并探索它与我们的新代码的交互。

  • In a very large legacy system with a few applications and many batches running in the background several times a day, the overall behavior of the system was quite nebulous. We created a small-scale, simplified Java emulation of the most important batch so that we could better understand it and explore its interactions with our new code.

  • 在两家具有丰富领域的初创公司中,我们结对编程了几天,以创建一个仅适用于一个非常简单的案例的小型模型。这使我们有机会快速探索和绘制该领域,发现主要问题和利害关系,扩大词汇量,并就整个系统的共同愿景达成一致。从那个小规模的系统开始,后来的讨论有了一个具体的参考代码来作为讨论的基础。我们发现这确实是一个我们可以在对话中指出的沟通工具。

  • In two startups with a rich domain, we pair-programmed for a few days to create a small-scale model that worked for just one very simplified case. This gave us an opportunity to quickly explore and map the domain, discover the main issues and stakes, grow a vocabulary, and agree on a shared vision of the whole system. From that small-scale system, later discussions had a concrete reference code to ground the discussions. We found that this was really a communication tool we could point at during conversations.

在一家一切都需要很长时间的大公司里,以“概念验证”的名义创建一个小规模模型是一个很好的替代方案,可以替代进行无休止的研究,除了幻灯片和幻想之外什么也没有。对工作代码的关注有助于收敛,并使回避棘手问题变得更加困难。您可能在一开始就已经构建了概念证明。但你会保留它们以供日后解释吗?

At a big company where everything takes ages, creating a small-scale model under the name “proof of concept” is a great alternative to doing never-ending studies delivering nothing but slides and illusions. The focus on working code helps converge and makes it harder to elude the tough questions. You probably already build proofs of concepts at the beginning. But do you keep them for their explanatory power later?

小规模模拟的理想特性

The Desirable Properties of a Small-Scale Simulation

小规模模拟必须具备以下特征:

A small-scale simulation must have the following characteristics:

  • 它必须足够小以适合正常人或开发人员的大脑:这是最重要的属性,它意味着模拟不会考虑原始系统的所有内容。

  • It must be small enough to fit in the brain of a normal person or of a developer: This is the most important property, and it implies that the simulation will not account for everything of the original system.

  • 您必须能够修改它,并且它必须能够吸引交互式探索:代码应该易于部分运行,只需能够对类或函数执行某些操作,而无需重建完整的模拟。

  • You must be able to tinker with it, and it must be inviting for interactive exploration: The code should be easy to run partially, by just being able to do something with a class or function without having to rebuild the full simulation.

  • 它必须是可执行的,才能在运行时展示动态行为:模拟必须通过其执行来预测结果,并且您必须能够轻松观察它,即使在计算期间,如果可能的话,在调试模式下,通过跟踪或仅通过运行其独立的阶段。

  • It must be executable to exhibit the dynamic behavior at runtime: The simulation must predict results through its execution, and you must be able to observe it easily, even during the computation, if possible, in debug mode, with traces or just by running its phases independently.

可执行且以现实方式工作的小型软件项目对于系统推理很有价值。您可以通过在代码中观察它的静态结构来推理它。您还可以通过再创建一个测试用例或在 REPL 中与其交互来修改它。

A small-scale software project that is executable and works in a realistic fashion is valuable for reasoning on the system. You can reason on its static structure just by observing it in the code. You can also tinker with it by creating one more test cases or by interacting with it in a REPL.

这种方法也可以作为不切实际的遗留系统或外部系统的廉价代理。您可以运行其模拟来了解其与您正在执行的操作之间的关系,而不是运行依赖于数据库状态并且在各处产生大量副作用的复杂批处理。

This approach is also useful as an inexpensive proxy to impractical legacy or external systems; instead of running a complex batch that depends on the state of the database and that has numerous side effects all over the place, you can run its emulation to get a grasp of its effect in relationship to what you’re doing.

简化系统的技术

Techniques to Simplify a System

为了实现小规模模拟,您需要积极简化整个系统,并专门关注重要的一两个方面。就像任何其他文档一样,系统文档应该很好地解释 1 件事,而不是糟糕地解释 10 件事。(已经有真正的系统了。)请注意,您仍然可以决定构建多个小规模模拟,例如针对每个要解释的重要点进行一个模拟。

To achieve a small-scale simulation, you want to simplify aggressively the full system, with an exclusive focus on the one or two aspects that matter. Just like any other documentation, documentation of a system should explain 1 thing well rather than explain 10 things badly. (There’s already the real system for that.) Note that you can still decide to build more than one small-scale simulation, such as one for each important point to explain.

简化的小规模系统会丢失很多细节,并且不会显示很多本来有价值的知识。这种简化比看起来更难做到,因为当你依附于你构建的系统时,你想讲述它所有有趣的方面,但你必须克制住这样做并学会集中注意力。

A simplified small-scale system will lose a lot of details and will not show a lot of otherwise valuable knowledge. This simplification is harder to do than it seems because when you’re attached to a system you’ve built, you’d like to tell about all its interesting facets, but you have to refrain from doing so and learn to focus.

有趣的是,用于构建小规模模拟的技术是您已经用于创建方便测试的技术。

Interestingly, the techniques used to build a small-scale simulation are the techniques you already use to create convenient tests.

具体来说,您可以通过多种方式简化系统,通常是决定忽略一个或多个分散注意力的问题:

Concretely, you can simplify a system in many ways, always by deciding to ignore one or many distracting concerns:

  • 策展:放弃功能必须完整的想法。删除所有与当前焦点无关的成员数据。忽略支线故事和次要内容,例如与当前焦点不相交的特殊情况。

  • Curation: Give up the idea that it has to be feature complete. Get rid of all the member data that is not central to the current focus. Ignore side stories and secondary stuff like special cases that don’t intersect the current focus.

  • 模拟、存根和间谍:放弃执行所有计算。相反,使用通常的测试伙伴来完全摆脱所有非中心相关的子部分。使用内存中集合代替中间件并模拟第三方。

  • Mocks, stubs, and spies: Give up performing all the computations. Instead, use the usual test companions to totally get rid of all the non-centrally relevant subparts. Use in-memory collections instead of middleware and simulate third parties.

  • 近似值:放弃严格的准确性,只选择看起来足够好的实际准确性,例如没有数字的正确值或 1% 的正确率。

  • Approximation: Give up on strict accuracy and settle only on realistic accuracy that looks good enough, such as the right value without the digits or 1% correct.

  • 更方便的单位:放弃使用实际数据进行模拟真正投入生产的能力。例如,如果日期仅用于确定给定数据之前或之后是否发生某些事情,则可以用纯整数替换手动操作很麻烦的日期。

  • More convenient units: Give up the ability to really put in production the simulation with the actual data. For example, if the dates are only used to decide if something happens before or after a given data, you might replace the dates that are cumbersome to manipulate by hand with plain integers.

  • 蛮力计算:放弃与当前重点无关的优化。相反,使用最容易掌握或最具解释力的算法来进行模拟。

  • Brute-force calculation: Give up the optimizations that are not central to your current focus. Instead, make the simulation work using the algorithm that’s the simplest to grasp or the one with the most explanatory power.

  • 批处理与事件驱动:将原始事件驱动方法转变为批处理模式,或者相反,如果它更易于编码和理解,假设这种方法不是当前焦点的核心。

  • Batch versus event driven: Turn the original event-driven approach into a batch mode, or the other way around, if it’s simpler to code and understand, assuming that this approach is not central to the current focus.

构建小规模模拟已经是乐趣的一半

Building a Small-Scale Simulation Is Half the Fun

通过创建小规模模拟,您可以学到很多东西。你必须澄清你的想法,没有什么比简单、有效的代码来实现这一点更困难的了。

You learn a lot by creating a small-scale simulation. You have to clarify your thoughts, and nothing’s more demanding than simple, working code to force that.

从设计的角度来看,通过细节来关注本质可以给你带来很多见解,可以帮助你改进原始系统的设计。例如,如果您可以在模拟中用整数替换日期,则原始函数实际上并不需要对日期进行操作,而是可以对任何类似的内容进行操作。

From a design perspective, cutting through the details to focus on the essentials gives you a lot of insights that can help you improve the design of the original system. For example, if you can replace dates with integers in the simulation, the original functions don’t really need to operate on dates but can operate on anything comparable.

如果模拟可以在没有所有这些分散注意力的方面的情况下进行,这也意味着原始设计应该遵循单一责任原则,从而分离所有关注点。您知道,当您达到这种状态时,您可以通过重用原始系统中的相同代码来创建小规模模拟,只需组装其元素的简单子集即可。

If the simulation can work without all these distracting aspects, this also means the original design should follow the single responsibility principle and, therefore, separate all the concerns. You know when you’ve reached that state when you can create your small-scale simulation by reusing the same code from the original system just by assembling a naive subset of its elements.

在启动项目的背景下使用的这个想法在文献中以各种名称为人所知:阿利斯泰尔·科伯恩(Alistair Cockburn)谈论行走的骨架17 号

This idea used in the context of starting a project is known under various names in the literature: Alistair Cockburn talks about a walking skeleton.17

17.科伯恩,阿利斯泰尔。清晰可见:小型团队的人力方法论。波士顿:培生教育公司,2004 年。

17.Cockburn, Alistair. Crystal Clear: A Human-Powered Methodology for Small Teams. Boston: Pearson Education, Inc., 2004.

这个想法在很多方面也与Dave Hoover 和 Adewale Oshineye 所著的《Apprenticeship Patterns: Guidance for the Aspiring Software Craftsman》一书中描述的模式可破坏玩具相似。18小规模模拟可用于比实际系统更快地尝试事物。这对于快速尝试两种或三种相互竞争的方法以基于实际事实而不是意见来决定最佳方法非常有用。

This idea is also similar in many aspects to the pattern breakable toys described in the book Apprenticeship Patterns: Guidance for the Aspiring Software Craftsman by Dave Hoover and Adewale Oshineye.18 A small-scale simulation can be used to try things much faster than the actual system. This comes in handy for trying two or three competing approaches quickly to decide on the best one, based on actual facts rather than on opinions.

18.大卫·H·胡佛和阿德瓦勒·奥希内耶。学徒模式:有抱负的软件工匠指南。加利福尼亚州塞巴斯托波尔:O'Reilly Media, Inc. 2009。

18.Hoover, David H., and Adewale Oshineye. Apprenticeship Patterns: Guidance for the Aspiring Software Craftsman. Sebastopol, CA: O’Reilly Media, Inc. 2009.

这样一个可修改的系统非常有价值,因为新加入者可以建立自己的心理模型。如果像 Peter Naur 19所说的那样,很难使用文本等编纂规则来表达理论,那么通过无风险地使用它来形成自己的关于系统的理论的能力会有所帮助。这就是孩子们学习所有物理定律的方式。

Such a tinkerable system is very valuable because new joiners can build their own mental models about it. If, as Peter Naur19 claims, it’s very hard to express a theory using codified rules such as text, having the ability to form your own theories about a system by just playing with it without risk can help. This is how kids learn about all the laws of physics.

19.彼得·诺尔。“编程作为理论构建。” 微处理和微编程15,没有。5(1985):253-261。

19.Naur, Peter. “Programming as theory building.” Microprocessing and Microprogramming 15, no. 5 (1985): 253-261.

系统隐喻

System Metaphor

如果您进行培训,您就会知道向您不认识的受众解释某些内容是多么困难。你需要确定他们已经知道什么,这样你就可以在此基础上继续发展。

If you run trainings, you know how hard it is to explain something to an audience you don’t know. You need to determine what they know already so you can build on that.

隐喻通过利用大多数人已经熟悉的事物来发挥作用,因此可以更有效地解释新事物。

Metaphors get their power by leveraging on things most people are already familiar with, so it’s possible to explain new stuff more efficiently.

通过谈论另一个系统来解释一个系统

Explaining a System by Talking About Another System

一个关于系统如何运作的简单共享故事,一个隐喻。

A simple shared story of how the system works, a metaphor.

—C2 维基百科,http://c2.com/cgi/wiki?SystemMetaphor

—C2 Wiki, http://c2.com/cgi/wiki?SystemMetaphor

当我解释幺半群以及它们如何组成时,我通常使用有形世界的隐喻,例如我可以堆叠的真正的啤酒杯或可以堆叠的椅子或任何其他可堆叠的东西。这有助于阐明幺半群可组合性的思想,而且很有趣,对学习也很有好处。

When I explain monoids and how they compose, I usually use the metaphor to the tangible world, such as real glasses of beers that I can stack or chairs that can be stacked or anything else that is stackable. This helps elucidate the idea of monoidesque composability, and it’s fun, which is also very good for learning.

我们都熟悉的暗示性隐喻包括装配线、输水管道、乐高积木、铁轨上的火车和物料清单。

Suggestive metaphors we’re all familiar with include an assembly line, a water pipeline, Lego building blocks, a train on its rails, and a bill of materials.

系统隐喻在极限编程 (XP) 中用于统一体系结构并提供命名约定。

The system metaphor was used in Extreme Programming (XP) to unify an architecture and provide naming conventions.

著名的极限编程项目 C3“是作为一条生产线构建的”,而另一个著名的 XP 项目 VCAPS“的结构类似于物料清单”。每个选定的隐喻都是一个由名称、关系和角色组成的系统,它们共同努力实现一个共同的目标。使用隐喻时,您可以调用受众的所有先验知识,以便在所考虑的系统上下文中重用。您知道,装配线通常是线性的,多台机器沿着传送带排列,将零件从一台机器移动到另一台机器。您还知道上游的任何缺陷都会导致下游的缺陷。

The famous Extreme Programming project C3 “was built as a production line,” and the other famous XP project VCAPS “was structured like a bill of materials.” Each chosen metaphor acts as a system of names, relations, and roles working together toward a shared purpose. When using a metaphor, you invoke all the prior knowledge of the audience to be reused in the context of the system being considered. You know that an assembly line is typically linear, with multiple machines in line alongside a conveyor belt that is moving parts from one machine to the next. You also know that any defect upstream will result in defects downstream.

即使没有先验知识也很有用

Useful Even Without Prior Knowledge

上次我所在的团队构建了一个丰富的现金流计算引擎,能够从任何复杂的金融工具中重新创建现金,该团队使用了模块化合成器的比喻。现在我不得不承认,并不是每个人都熟悉模块化合成器,但在那个团队中,有几个人知道它们。有趣的是,这个比喻甚至对那些不了解它们的人也有帮助。

The last time I was on a team that built a rich cash flow calculation engine that was able to re-create cash from any complicated financial instrument, the team used the metaphor of a modular synthesizer. Now I have to admit that not everyone is familiar with modular synths, but in that team, several people knew about them. The interesting thing is that the metaphor helped even the people who didn’t know about them.

有趣的是,即使对于那些不知道它的人来说,隐喻仍然在某种程度上有用,就像一种冗余机制一样。想象一下,您正试图在脑海中将现金流引擎想象为解释器模式,但您并不完全确定自己是否正确。现在,如果我解释什么是模块化合成器(“一组充满按钮和旋钮的电子模块,通过插入其中的跳线以任意方式连接在一起”),它应该会有所帮助。每个连接器之间的跳线组合几乎是无限的,可以产生各种各样的声音,就像金融引擎的情况一样。

It’s intriguing that a metaphor remains somehow useful even for people who don’t know it, just as a redundancy mechanism. Imagine that you’re trying to mentally picture the cash flow engine as an interpreter pattern, and you’re not fully sure you got it right. Now, if I explain what a modular synth is (“a set of electronic modules full of buttons and knobs, wired together in an arbitrary fashion via patch cords plugged into them”), it should help. The patching combinations between every connector are nearly infinite for a large variety of sounds to be produced, just as is the case with the financial engine.

另一个隐喻中的隐喻

A Metaphor in Another Metaphor

一个很好的比喻是一个具有一定生成能力的模型:如果我知道停止生产线的成本非常昂贵,我就会想知道这是否以及如何转化为软件系统。确实如此,就像在生产线上一样,我们应该对投入的原材料进行严格的验证,以保护生产线。但这个比喻在这方面可能不成立,仅此而已。

A good metaphor is a model with some generative power: If I know that stopping a production line is very expensive, I can wonder if and how that would translate into a software system. It does, and just as on a production line, we should perform strict validation of the raw materials in inputs to protect the line. But the metaphor may not stand on this aspect, and that’s it.

存在的文化越普遍,可用作隐喻的想法就越多。当您知道什么是销售漏斗(见图12.6)时,您可以通过谈论它来解释电子商务系统的关键方面,以及从访问者到查询、到建议、到新客户的连续业务阶段。之所以称为漏斗,是因为每个阶段的体积都会显着减小。

The more common culture that exists, the more ideas available to use as metaphors. When you know what a sales funnel is (see Figure 12.6), you can talk about it to explain key aspects of an e-commerce system, with its successive business stages from visitor to inquiries, to proposals, to new customer. It’s called a funnel because the volume at each stage decreases significantly.

图中显示了一个倒三角形,代表销售漏斗,分为三层,分别为 10 个客户、100 个潜在客户和 1000 个潜在客户。 (从下到上)。

图 12.6 销售漏斗

Figure 12.6 A sales funnel

当您进行架构时,这些知识会派上用场,因为它告知可扩展性推理:上游阶段(如目录)比下游阶段(如支付)需要更多的可扩展性。

This knowledge comes in handy when you’re doing architecture, as it informs scalability reasoning: The upstream stage, like the catalog, needs more scalability than the downstream stage, like the payment.

概括

Summary

软件架构的文档不一定是专有建模工具中的幻灯片或模型。最好以活生生的文档方式完成,在系统本身中就地完成,并组织成人们熟悉的架构景观。

Documentation of software architecture doesn’t have to be slideware or models in proprietary modeling tools. It is best done in a living documentation fashion, in situ in the system itself, and organized into an architecture landscape that people become familiar with.

这种方法的最终形式是一个透明且活跃的架构,也是测试驱动的。在这种情况下,重要的架构知识是可访问的,使任何人都可以随时快速做出明智的更改,并且借助自动断言,机器可以访问它以进行连续的现实检查和反馈。

The ultimate form of this approach is a transparent and living architecture that is also test driven. In this setting, the important architectural knowledge is accessible, enabling anyone to make informed changes quickly and any time, and it’s machine accessible for continuous reality checks and feedbacks, thanks to automated assertions.

第13章

Chapter 13

将活文档引入新环境

Introducing Living Documentation to a New Environment

活文档始于愿意改进文档或软件完成方式的当前状况的人。既然你正在读这本书,那么你就是那个人。您可能想要开始编写实时文档,因为您害怕丢失知识,或者因为您想更快地获取更容易获得的相关知识。您可能还想以它作为借口来展示团队制作软件的方式中的缺陷,例如缺乏深思熟虑的设计,并且您希望文档使其对每个人都可见且显而易见。

Living documentation starts with someone willing to improve the current state of affairs in either the documentation or the way software is done. Since you are reading this book, you are that person. You may want to start living documentation because you’re afraid to lose knowledge, or because you’d like to go faster with the relevant knowledge more readily available. You may also want to start it as a pretext to show flaws in the way the team is making software, such as in the lack of deliberate design, and you expect the documentation to make it visible and obvious to everyone.

困难的一步是找到令人信服的知识缺失案例。一旦您有了一个经过论证的案例,并且您可以使用其中一种实时文档方法来解决它,那么您就走在了正确的道路上。

A difficult step is to find a compelling case of missing knowledge. Once you have a demonstrated case and provided that you can solve it with one of the living documentation approaches, you’re on the right track.

卧底实验

Undercover Experiments

如果您觉得自己对活文档感兴趣,您可能需要温和而缓慢地开始,不要大声喧哗,最重要的是,不要征求授权。我们的想法是,无论以何种方式进行记录,都是专业开发人员自然工作的一部分。

If you feel alone in your interest in living documentation, you may want to start gently and slowly, without making a lot of noise about it and, most importantly, without asking for authorization. The idea is that documenting, whatever way it is done, is part of the natural work of a professional developer.

因此:将活文档内容自然地引入日常工作的一部分。在做出设计决策、意图和基本原理时就开始注释它们。当有一些空闲时间或真正需要文档时,请使用分配的时间创建简单的文档自动化,例如简单的生活图表或基本术语表。保持足够简单,使其在几个小时或更短的时间内完成。不要将其视为一场革命,而是将其视为高效做事的自然方式。强调本书的好处,而不是理论。

Therefore: Introduce nuggets of living documentation naturally as part of the daily work. Start annotating your design decisions, intents, and rationales at the time you make them. When there is some slack time or a genuine need for documentation, use the allotted time to create simple documentation automation such as a simple living diagram or a basic glossary. Keep it simple enough to have it work in a few hours or less. Don’t talk about it as a revolution but just as a natural way to do things efficiently. Emphasize the benefits, not the theory from this book.

当然,当人们对这种方法更感兴趣时,您可以将实时文档作为主题进行讨论,并将他们引导到这本书。

Of course, when people become more interested in the approach, you can talk about living documentation as a topic and direct them to this book.

官方的野心

Official Ambition

引入活文档的另一种方式是通过官方野心,尽管我不推荐以此作为起点。

Another way to introduce living documentation is through an official ambition, though I don’t recommend this one as a starting point.

走官方路线通常从管理层开始,或者至少需要管理层的支持。文档通常是经理们感到沮丧和焦虑的根源,因此经理们比开发团队更经常提倡这个话题。

Going the official route usually begins with management, or at least requires management sponsorship. Documentation is often a source of frustration and anxiety for managers, and therefore this topic is often promoted even more by managers than by the development team.

拥有赞助商是个好消息:您有专门的时间,甚至可能有一个团队来实施。与之相对应的是,作为官方的野心,它将受到高度关注和密切监控,并且会面临快速交付看得见的东西的压力。这种压力可能会迫使成功而危及主动性。但活生生的文档是一次发现之旅,它有实验的一面,在你自己的背景下没有明确的成功之路。您必须尝试一些事情,确定一些事情不适用,并根据自己的情况调整一些事情。最好不要受到高层的过度审查。

Having a sponsor is good news: You have dedicated time and perhaps even a team to implement. The counterpart is that as an official ambition, it will be highly visible and closely monitored, and there will be pressure to deliver something visible quickly. This pressure may endanger the initiative by forcing success. But living documentation is a discovery journey, there’s an experimental side to it, and there is no clear path to success in your own context. You’ll have to try things, decide that some are not applicable, and adjust some things to your own cases. This is best done without excessive scrutiny by higher-ups.

因此,我建议您从秘密实验开始,只有在您找到环境中的实时文档的最佳点后,才将这个主题作为官方目标进行推广。

I therefore recommend starting with undercover experiments and promoting the topic as an official ambition only after you’ve found the sweet spots of living documentation in your environment.

新事物必须有效并且必须被接受

New Things Have to Work and Have to Be Accepted

我建议的典型路径是从创造食欲开始,然后尝试快速机会展示好处,然后从那里开始成长:

A typical path I advise is to start with creating appetite, then try for quick opportunities to show benefits, and then to grow from there:

  1. 首先要在更多受众中提高认识。实现这一目标的一个好方法是通过内容丰富且有趣的全场演讲。重点不是解释如何做事,而是展示与当前状况相比,生活如何可以变得更好。Nancy Duarte 的《Resonate 1》一书充满了关于如何做好这一点的建议。听取会议结束时和几天后的反馈,以决定是否有兴趣进一步开展。否则,您可能想在几周或几个月后重试,或者您可能决定先卧底。

    1.杜阿尔特,南希。产生共鸣。霍博肯:约翰·威利父子公司,2010 年。

  2. Start by creating awareness in the larger audience. A great way to do this is through an all-audience talk that is informative and entertaining. The point is not to explain how to do things but to show how life could be better compared to the current state of affairs. Nancy Duarte’s book Resonate1 is full of advice on how to do this well. Listen to the feedback at the end of the session and a few days later to decide whether there is appetite to go further. Otherwise, you may want to try again some weeks or months later, or you might decide to go undercover first.

    1.Duarte, Nancy. Resonate. Hoboken: John Wiley and Sons, Inc. 2010.

  3. 花一些时间与团队或有影响力的团队成员一起确定哪些知识最值得记录。从那里开始,建议快速获胜,尝试作为待办事项中的短期项目或作为致力于改进的时间的一部分。回顾也是考虑活文件问题和提出行动建议的好时机。关注许多人认为重要的实际需求非常重要。

  4. Spend some time with the team or an influencer team member to identify what knowledge most deserves to be documented. From there, propose quick wins to try as short items in the backlog or as part of time dedicated to making improvements. Retrospectives are also a good time to consider living documentation issues and propose actions. It is important to focus on real needs that many people find important.

  5. 在短时间内构建一些有用的东西并像处理任何其他任务一样对其进行演示。收集反馈、改进并集体决定是现在还是以后扩展(如果需要)。

  6. Build something useful in a short period of time and demo it as you would any other task. Collect feedback, improve, and collectively decide on whether to expand now or later, if needed.

轻轻开始

Starting Gently

作为一名顾问,我经常与各种规模公司的团队一起工作。当他们要求创建更多文档时,我倾向于建议几个步骤。

As a consultant, I regularly sit with teams in companies of all sizes. When they ask to create more documentation, I tend to suggest several steps.

首先,我提醒他们,交互式和面对面的知识传递必须是文档化的主要手段。

First of all, I remind them that interactive and face-to-face knowledge transfer must be the primary means of documentation, before anything else.

接下来,我告诉他们,我们可以考虑用一些技术来记录每个人都必须知道的、每个新人都必须学习的、从长远来看很重要的关键知识。

Next, I tell them, we can then consider techniques to record the key bits of knowledge that have to be known by everyone, that every newcomer has to learn, and that matter in the long run.

此时有人可能会说:“让我们把这些东西写在我们的 wiki 中吧。” 这很好,只要我们明白维基是常青内容、不经常变化的知识的好地方。对于其他一切,我们可以做得更好。

At this point someone might say, “Let’s write that stuff in our wiki.” That is fine, as long as we understand that a wiki is a nice place for evergreen content, for knowledge that does not change often. For everything else, we can do better.

从哪儿开始?我喜欢很快地提出各种想法来扫描团队成员的兴趣。例如,我将简要提及以下各项:

Where to start? I like to mention various ideas very quickly to scan the interest of the team members. For example, I would mention briefly each of the following:

  • 我们可以添加一个简单的自述文件来描述该项目的内容。

  • We could add a simple README describing what the project is about.

  • 我们可以在项目的基本目录中添加一个简单的决策日志作为 Markdown 文件,回顾自项目启动以来的三到五个主要架构和设计决策。

  • We could add a simple decision log as a Markdown file in the base directory of the project, with a recap of the three to five main architectural and design decisions since its inception.

  • 我们可以使用自定义注释或属性来标记代码,以突出显示关键地标或核心概念。与 IDE 中的引用搜索相结合,这成为提供代码库观光地图的简单而有效的方法。

  • We could tag the code with a custom annotation or attribute to highlight the key landmarks or the core concepts. Combined with the search by reference in the IDE, this becomes a simple yet effective way to provide a sightseeing map of the code base.

  • 同样,我们可以使用引导注释或属性来标记代码,以提供一种简单的方法来跟踪请求或以线性方式跨不同的代码片段、跨不同的层或模块进行端到端的处理。同样,这依赖于 IDE 中的引用搜索。

  • On a similar note, we could tag the code with guided tour annotation or attributes to provide a simple way to follow a request or processing end-to-end across the various fragments of code, across the various layers or modules, in a linear fashion. Again, this relies on search by reference in the IDE.

  • 我们可以将最重要的餐巾纸草图转换为决策日志文件中的 ASCII 图表。

  • We could turn the most important napkin sketch into an ASCII diagram in the decision log file.

这个列表故意只包含可以在短时间内完成和提交的事情。例如,我合作过的一个团队能够添加并提交包含过去五个关键决策的决策日志,标记三个关键地标以及两小时内包含五个步骤的导游。这包括分别为关键地标和导游创建两个自定义属性,确保 IDE 中的搜索运行良好,并确保 Markdown 渲染在 TFS 中良好。

This list deliberately contains only stuff that can be done and committed within a short period of time. For example, a team I’ve worked with has been able to add and commit a decision log with five past key decisions, marking three key landmarks plus a guided tour with five steps within two hours. This included the creation of the two custom attributes for the key landmarks and the guided tour, respectively, ensuring that the search in the IDE worked well, and ensuring that the Markdown rendering was fine in TFS.

此阶段的目标是通过快速达到有吸引力的结果来提高意识和兴趣。目标是听到,“哇,我真的很喜欢这种方法。我现在已经上瘾了!”

The goal at this stage is to create awareness and interest by reaching attractive results quickly. The goal is to hear, “Wow, I really like that approach. I’m hooked now!”

另一个目标是让团队成员在完成这些简单的步骤后就已经体验到“超越文档的效果”:“哎呀,我现在意识到我们的结构是多么草率和半成品。” 两个小时的时间真是太棒了!

Another goal is for the team members to already experience the “beyond documentation effect” after just going through these simple steps: “Ouch, I now realize how sloppy and half-finished our structure is.” That’s a lot of goodness for two hours!

如果团队有真正的兴趣并且有一些空闲时间,您可以进一步尝试词云、动态术语表或动态图表。

Given genuine interest from the team and some available time, you can go further and try word clouds, a living glossary, or living diagrams.

变得更大、更可见

Going Big and Visible

在一个温和的开始之后,你可能想要有更大的野心。我并不是说这总是一个坏主意,但由于以下几个原因,它可能很危险:

After a gentle start, you might want have bigger ambitions. I’m not saying that is always a bad idea, but it can be dangerous for several reasons:

  • 可见的雄心通常需要通过一系列成果甚至关键绩效指标来展现象征性的进展。但“成为 40% 的活文件”意味着什么吗?仅仅为了做到这一点而制作活生生的文档最终会让这种方法名誉扫地。

  • Visible ambitions usually need to exhibit symbolic progress shown by a quantity of outcomes or even KPIs. But does it mean anything “to be 40% living documentation”? Doing living documentation just to do it will eventually discredit the approach.

  • 收益可能会推迟几个月,如果衡量超过三个月,则可能很难显示投资回报。

  • The benefits can be deferred by months, and it can be difficult to show the return on investment if measured over three months.

  • 如前所述,在您的特定环境中应用本书中的技术时,可能需要进行各种调整;与此同时,这些调整可能会被视为失败。

  • As mentioned earlier, it may take various adjustments when applying the techniques from this book in your particular context; these adjustments may be perceived as failures in the meantime.

  • 对团队有用的东西可能不是管理层所期望的。如果是这种情况,请站在管理层的角度思考:什么会让您对文档感到满意?如果您能让非开发人员能够访问以前隐藏的知识,这对每个人来说可能都是一件好事。管理者将能够根据从代码库中提取的客观事实自行判断某些事情。当您设置动态图表或其他机制时,您有机会以促进您的议程的方式进行策划和演示(例如,鼓励好事或警告坏事)。

  • What’s useful for the team may not be what management expected. If this is the case, put yourself in management’s shoes: What would make you happy with respect to documentation? If you can make previously hidden knowledge accessible to nondevelopers, it might be a good thing for everyone. Managers will be able to judge something by themselves, based on objective facts extracted from the code base. And when you set up the living diagram or some other mechanism, you have an opportunity to do the curation and the presentation in a way that promotes your agenda (for example, to encourage good things or to warn against bad ones).

无论如何,请记住,无论是否存在,文档本身并不是目的,而是加速交付的一种手段。由于通过实时文档可以轻松获得知识,因此可以更快地做出决策,从而可以直接加速交付。它也可以是间接的,当创建文档时,可以提高人们对系统中、思维中或利益相关者之间沟通中的所有草率问题的认识。通过解决根本原因,您可以改进整个系统,从而加速交付。

In any case, remember that documentation, living or not, is not an end in itself but a means to accelerating delivery. This acceleration of delivery can be direct, when decisions are made faster thanks to the knowledge readily available through the living documentation. It can also be indirect, when creating the documentation raises awareness on everything sloppy in the system, in the thinking or in the communication between the stakeholders. By fixing the root cause, you improve the whole system, which in turn accelerates delivery.

案例研究:向团队成员介绍活文档的故事

Case Study: A Tale of Introducing Living Documentation to a Team Member

我遇到了一位有兴趣了解更多有关活文档的团队成员。他只是好奇,并不相信,但他的好奇心是一个好的开始。

I met with a team member who was interested in learning more about living documentation. He was just curious, not convinced, but his curiosity was a good start.

对话第一

Conversations First

我倾向于以对话的方式开始用问题来解释活文档。我没有解释什么是活文档,而是首先将自己置于另一个愿意了解该项目的团队成员的立场上。我请团队成员告诉我当前的项目,然后我告诉她我要记笔记并在活动挂图上勾勒出我们所说的内容。然后我开始问这样的问题:

I tend to start explaining living documentation with questions in a conversational style. Instead of explaining what living documentation is, I start by putting myself in the shoes of another team member willing to learn about the project. I ask the team member to tell me about the current project, and I tell her I’m going to take notes and sketch what we say on a flipchart. Then I begin to ask questions like these:

该项目的名称是什么?它的目的是什么?它是为谁而设?

What’s the name of the project? What is its purpose, and who is it for?

什么是生态系统,包括外部系统和外部参与者?总体输入和输出是多少?

What is the ecosystem, including the external systems and external actors? What are the overall input and outputs?

执行风格是什么?它是交互式的、每晚批处理、GitHub 挂钩吗?主要语言是什么:Ruby、Java、Tomcat 还是其他语言?

What’s the execution style? Is it interactive, a nightly batch, a GitHub hook? What’s the main language: Ruby, Java, Tomcat, or something else?

到目前为止,这些都是标准问题。答案自然而然。但我接着问:

These are all standard questions so far. Answers come naturally. But then I ask:

您认为核心领域是什么?

What is the core domain, in your opinion?

这令人惊讶。团队成员需要一些时间来思考。令她惊讶的是,经过几个月的项目后,答案并不明显。

This comes as a surprise. The team member needs some time to think about it. She is surprised that the answer was not obvious, after several months on the project.

“哦,既然你提到了这一点,”她说,“我意识到我们的核心领域可能是我们在向外部合作伙伴提供的提要中插入指向我们系统的深层链接的方式,以便他们为我们带来合格的入站链接网络流量。我以前没有这样想过,我不确定团队中的每个人都意识到这一点。”

“Oh, now that you mention that,” she says, “I realize that our core domain is probably the way we insert deep links that point to our system in the feed we provide to the external partners, so that they bring us qualified inbound web traffic. I didn’t think about it this way before, and I’m not sure everyone on the team is aware of it.”

“这个深层链接是整个项目存在的理由吗?” 我问。

“Is this deep link thing the raison d’être for the whole project?” I ask.

“是的,”她说,“绝对是的。”

“Yes,” she says, “absolutely.”

我进一步追问:“你认为每个人都应该知道这一点吗?

I press further: “Do you think everyone should know about that?

“显然,是的,”她说。

“Obviously, yes,” she said.

“所以我们应该在某个地方记录下来?” 我问。

“So we should document that somewhere?” I ask.

“当然!” 她回答道。

“Of course!” she replies.

第一次汇报

The First Debriefing

通过对话了解到我感兴趣的内容后,我可以举行汇报会来介绍活文档的基本概念。

After I learn what I am interested in through having conversations, I can hold a debriefing to introduce the basic concepts of living documentation.

活文档主要是通过对话来分享知识。到目前为止,我在对话中的目标是快速了解很多对我来说重要的事情,而不是在任何其他事情上浪费时间。互动对话和高带宽的谈话是无与伦比的,特别是在活动挂图的支持下,这有助于我确保理解对方的信息。

Living documentation is primarily about having conversations to share knowledge. My goal in the conversations so far was to learn a lot of what matters to me, quickly and without wasting time on any other stuff. Interactive conversations and the high bandwidth of talking are hard to beat, especially with the support of a flipchart, which helps me ensure that I understand the other person’s message.

我现在可以介绍的第二点是,我们到目前为止谈到的一些知识需要以持久化的形式记录下来。值得庆幸的是,到目前为止,大部分知识都随着时间的推移而保持稳定。这是幸运的,所以在这种情况下我们可以使用任何形式的常青文档:wiki、文本等。但我们必须确保不要混合任何不稳定和短暂的知识,否则我们会立即失去常青文档的好处,这些文档不需要任何维护,但永远(或在很长一段时间内)保持真实。 。

The second point I can introduce now is that some of the knowledge we talked about so far needs to be recorded in a persistent form. And the good thing to recognize is that most of this knowledge so far is stable over time. This is lucky, so in this case we can use evergreen documents in any form: wiki, text and so on. But we must make sure not to mix any volatile and short-lived knowledge, or we immediately loose the benefits of evergreen documents, which are documents that don’t need any maintenance yet remain true forever (or for a very long period of time).

这里已经有第三点:我们发现的“深度链接”概念是在线文献中已经记录的标准概念。因此,它是现成的知识。我们可以在网络上链接到它,因此无需再次解释它是什么。我们很懒。

There is a third point here already: The concept of “deep linking” we uncovered is a standard concept already documented in the online literature. As such, it’s readymade knowledge. We can link to it on the web, so there is no need to explain what it is again. We’re lazy.

我们在这个例子中开始看到的最后一点是,通过关注文档,即使是有知识的人也会在这个过程中学习并获得额外的认识。这说明了“超越文档”的好处,这可能是活文档的最大价值。

One last point we begin to see in this example is that by paying attention to the documentation, even the person with the knowledge also learns and gains additional awareness in the process. That illustrates the benefits “beyond documentation,” and it’s probably the biggest value of living documentation.

是时候谈谈代码了

Time to Talk About the Code

在我对上下文和问题方面提出了所有问题之后,我想了解更多有关解决方案方面的信息 - 换句话说,代码。所以我问代码是如何组织的。

After all my questions on the context and on the problem side, I’d like to know more about the solution side—in other words, the code. So I ask how the code is organized.

然后我们在活动挂图上绘制文件夹层次结构,结构非常接近六边形架构。这时我稍微激怒了团队成员:“想象一下,项目交付后你们都走了,而且没有预算留下来留住你。一年后,该项目必须恢复,并且需要交付更多功能,因此组建了一个新团队。您认为新团队会降低当前系统的风险有哪些?”

Then we draw the folder hierarchy on the flipchart, and the structure is very close to the hexagonal architecture. At this point I provoke the team member a bit: “Imagine that you’re all gone after the project is delivered, and there is no budget left to keep you. Then a year later, the project has to resume, and additional features need to be delivered, so a new team is formed. What risks do you see that the new team will degrade the current system?”

在这种虚构的情况下,团队成员更容易回答:“我想说刚接触该项目的初级开发人员可能会将业务逻辑放在 REST 端点中,但这会很糟糕。”

In this fictitious situation, it’s easier for the team member to answer: “I’d say junior developers new to the project might put business logic in the REST endpoints, and that would bad.”

“当然,”我说。“那会很糟糕。不过,我认为今天应该没有必要说这个,因为现在专业开发人员应该都知道这一点。”

“Sure,” I say. “It would be bad. Still, I think there should be no need to say this today, as it’s supposed to be known by professional developers these days.”

该团队成员表示,他们所做的一切都非常标准,没有什么意外。对我来说,这意味着不需要所有标准内容的文档。此外,代码相当干净,并且显示了它是如何完成的。但是,它没有说明为什么这样做。

The team member says that they were doing everything pretty much standard, with no surprises. To me this means there’s no need for documentation on all the standard stuff. Also, the code is rather clean, and it shows how it’s done. However, it doesn’t tell why it’s done this way.

我问是否存在其他新团队成员意外降低系统设计的风险。

I ask whether there is a risk that any other new team members could degrade the design of the system by accident.

“事实上,”该团队成员说,“我们设计了一个包含列表和一个排除列表机制来过滤我们导出的内容,具体取决于外部合作伙伴。但我们这样做的方式是代码与外部合作伙伴完全无关。只有它们的配置是特定的。”

“In fact,” says the team member, “we designed an includelist and an excludelist mechanism to filter content that we export, depending on the external partner. But we did it in such a way that the code is totally agnostic with respect to the external partner. Only their configuration is specific.”

“你的意思是,在没有给出任何提示的情况下,完全不可知论的努力不一定会从代码中表现出来?” 我问。

“You mean that effort to be totally agnostic does not necessarily show from the code without giving any hint?” I ask.

她回答说:“是的,新来者很可能会很快IF围绕他或她需要支持的下一个合作伙伴特定行为添加一个声明,从而破坏设计。”

She replies, “Yes, it’s likely that a newcomer would quickly add an IF statement around the next partner-specific behavior he or she needed to support and would break the design as a result.”

决策日志和导游

Decision Logs and Guided Tours

我告诉团队成员,我们应该记录她刚才描述的设计决策。我们可以在源代码控制系统中项目根目录下的决策日志中以纯 Markdown 文件的形式执行此操作。它非常简洁:日期、决定、理由、后果。三句话就够了。

I tell the team member that we should record the design decision she just described. We can do that in a decision log as a plain Markdown file at the root of the project, in the source control system. It’s quite concise: date, decision, rationale, consequence. Three sentences would be enough.

还有什么?该项目的代码还不错,但仍然不够明显,无法在系统的所有阶段中跟踪用户请求。

What else? The code of the project is not bad, but it is still not obvious enough to follow a user request through all its stages in the system.

“为此我们可以进行导游,”我说。我将解释并展示如何创建自定义注释@GuidedTour来标记导游中的每个步骤。团队成员迅速设计出导游的最佳七个步骤,并在每个步骤上添加注释。介绍第一段游览需要20分钟。

“For that we could do a guided tour,” I say. And I explain and show how to create the custom annotation @GuidedTour to mark each step in the guided tour. The team member quickly devises the best seven steps of the guided tour and adds the annotation on each of them. It takes 20 minutes to introduce the first tour.

此外,通过游览,我发现整体行为的一个重要部分是对 Web 服务计算的缓存,以通读的方式这又是现成的知识,在线描述的!

Furthermore, through the tour, I find out that a significant part of the overall behavior is a cache on calculations on web services, in a read-through fashion: That’s ready-made knowledge again, that is described online!

然后,我们创建另一个自定义注释@ReadThroughCache来标记该知识,并提供简短的定义和网络上标准解释的链接。

We then create another custom annotation, @ReadThroughCache, to mark that knowledge, with a brief definition and a link to a standard explanation on the web.

经过 2.5 小时的交谈并创建注释来支持我们的第一个实时文档后,是时候从团队成员那里获得反馈了,我听到的内容听起来令人鼓舞:“我喜欢使用注释来记录文档的想法:它很轻量级,并且无需添加注释即可轻松添加”。要求这样做的权利。我可以单独开始,也可以在本地开始。相比之下,我认为其他技术(例如活图)更像是团队决策。链接到现成的知识可以节省时间,而且比我自己尝试以书面形式解释更准确。”

After 2.5 hours talking and creating annotations to support our very first living documentation, it’s time to get feedback from the team member, and what I hear sounds encouraging: “I like the idea of using annotations for documentation: It’s lightweight and easy to add without asking the right to do it. I can start solo and locally. In contrast, other techniques like a living diagram are more like team decisions, I think. And linking to ready-made knowledge saves time and is more accurate than if I tried to explain it myself in writing.”

我同意这一点,并提到这是嵌入式学习方法的一部分:“核心中的简单注释也向您的团队成员暗示了他们可能不知道的文献中有趣的想法。”

I concur, mentioning that it’s part of an embedded learning approach: “Simple annotations in the core also hint to your team members about interesting ideas in the literature they may not know otherwise.”

但她并不完全相信这种嵌入式学习适用于每个人:“是的,我的一些同事会意识到他们不知道,并且会好奇地想了解更多。有些人会阅读链接并自学,但有些人可能不会,而是会问我。”

But she’s not totally convinced that this embedded learning works for everyone: “Yes, some of my colleagues will realize that they don’t know and will be curious to learn more. Some will read the links and learn by themselves, but some will probably not and will ask me instead.”

“但我认为这是一个特点,”我说。“它会引发讨论,这可能对你们俩来说都是另一个学习的机会。”

“But I see that as a feature,” I say. “It invites discussion, and that’s another opportunity for learning, probably for both of you.”

对活文档的常见反对意见

Common Objections to Living Documentation

您想要开始制作活生生的记录并不意味着周围的每个人都同意。也许他们没有需要,或者他们没有看到好处。

Your desire to start doing living documentation doesn’t mean everyone around agrees. Perhaps they don’t have the need or they don’t see the benefits.

注释并不用于文档

Annotations Are Not Meant for Documentation

对活文档最常见的反对意见之一是注释不是为了文档而存在的。关于这一反对意见的对话可能会这样进行:

One of the most common objections to living documentation is that annotations are not meant for documentation. This is how a conversation about this objection might go:

团队成员:“我不喜欢在文档中使用注释,因为我不喜欢添加不执行的代码”

Team member: “I don’t like to use annotations for documentation because I don’t like adding code that does not execute”

[Obsolete]我:“你知道,当你将代码标记为or时,你就已经做到了@Deprecated.

Me: “You know, you do it already when you mark code as [Obsolete] or @Deprecated.

团队成员:“哦,是的。有道理。”

Team member: “Oh, yes. Fair point.”

我建议将“评论与注释”的选择定义为“好与坏”:“评论是坏的,应该避免;但如果要记录的信息确实很重要,那么就值得使用自己的自定义注释。”

I suggest framing the “comments versus annotation” choice as “good versus bad”: “Comments are bad and should be avoided; but if the information to record is really important, then it’s worth its own custom annotation.”

“我们已经这么做了”

“We Do It Already”

如果您召开大量技术会议,这可能表明您的内部文档可能会更好。

If you’re having lots of technical meetings, it MAY indicate that your internal documentation could be better.

—推特上的@ploeh

—@ploeh on Twitter

“我们已经这样做了”几乎是对任何事情的标准反对意见。在某种程度上,一切看起来都一样。

“We do it already” is a standard objection to almost anything. To some extent, everything looks like everything.

是的,您可能确实应用了本书中的许多实践,但是您真的采用了活文档方法吗?这里的关键词是故意的。如果你碰巧做了本书中讨论的一些事情,那也没关系,但如果是刻意去做这些事情就更好了。由您的团队决定将光标放在哪里并决定文档策略。这样的策略必须是紧急且深思熟虑的。它必须适合您的特定环境并被所有相关人员接受。

Yes, you probably do apply a number of the practices in this book, but are you really taking a living documentation approach? The key word here is deliberate. If you happen to do some of the things discussed in this book by chance, it’s fine, but it would be even better to do those things deliberately. It’s up to your team to decide where to put the cursor and to decide on a documentation strategy. Such a strategy has to be emergent and deliberate. It must fit your particular context and be accepted by everyone involved.

您的文档策略将混合您已经做过的实践,进一步推动其中一些实践,并引入听起来有前途的新实践。随着时间的推移,你将调整这一切,以最小的努力获得最大的好处。

Your documentation strategy will mix practices you already do, push some of them further, and introduce new practices that sound promising. And you will adjust all this over time to get the most benefits with minimal effort.

有人可能会说:“我们拥有我们需要的所有知识。”

Someone might say, “We have all the knowledge that we need.”

也许这个团队确实拥有所有知识,因为他或她比团队其他成员先到达那里。但其他人都这么舒服吗?

Perhaps this team does have all the knowledge because he or she was there before the rest of the team. But does everyone else feel so comfortable?

也许你只是讨厌文档,我完全可以理解这一点。但承认你不知道的事情很重要。

Perhaps you just hate documentation, and I can totally understand that. But it’s important to acknowledge what you don’t know.

将旧文档迁移到实时文档中

Migrating Legacy Documentation into Living Documentation

如果您有遗留文档,您可以利用它。这样做可以避免空白页综合症,并提供一个以新的视角回顾过去知识的机会。您有旧的 PowerPoint 文档吗?将其变成活生生的文档!将 PPT 中的知识放回到源代码中,无论它在哪里最合适:

If you have legacy documentation, you may leverage on it. Doing so avoids the blank page syndrome and offers an opportunity to review past knowledge in a new light. You have old PowerPoint documentation? Turn it into living documentation! Put the knowledge from the PPT back into the source code, wherever it fits best:

  • 愿景和目标可以以 Markdown 的形式放入 README 文件中。

  • Vision and goals could go into the README file, as Markdown.

  • 伪代码或序列图可以制作成纯文本图或 ASCII 图,或者您可以将其替换为对执行相同场景的测试的引用。

  • Pseudo-code or a sequence diagram could be made into a plain-text diagram or ASCII diagram, or you could just replace it with a reference to a test doing the same scenario.

  • 主要模块和方法的描述可以在源代码本身内通过一些类和模块级注释、注释和命名约定来完成。

  • Description of main modules and methods could be done within the source code itself, through some class- and module-level comments, annotations, and naming conventions.

  • 注释可以放在配置项中。

  • Comments can be put in config items.

请注意,所有这些知识都可以从共享驱动器和 wiki 中提取,以便在源代码管理中找到新家。

Notice that all this knowledge can be pulled from shared drives and wikis to find a new home in the source control.

同样令人震惊的是,当您转向实时文档时,原来集中在几个幻灯片或 Word 文档中的旧内容会遍布整个代码库。这听起来可能是一件坏事。有时您可能更愿意将一些概述幻灯片作为一个文档保存在一起。但对于大多数实用知识来说,最好的保存位置是尽可能靠近你需要它的地方。

It’s also striking that the old content that was all concentrated within a few slide decks or Word documents becomes spread all over the code base when you move to living documentation. This might sound like a bad thing. Sometimes you might prefer to have some overview slides kept together as one document. But for most of the practical knowledge, the best location to keep it is as close as possible to the place where you need it.

您可以对所有现有书面文档执行文档挖掘:电子邮件、Word 文档、报告、会议纪要、论坛帖子、各种公司工具(例如应用程序目录)的条目等。每当一条知识“经过这么长时间仍然听起来相关”时,它可能就值得保留。

You could perform documentation mining on all existing written documents: emails, Word documents, reports, meeting minutes, forum posts, entries into various company tools such as application catalogs, and so on. Every time a piece of knowledge “still sounds relevant after all this time,” it’s probably worth preserving.

在实践中,您将弃用或删除旧内容,可能会重定向到新位置以获取类似的知识或解释从现在开始如何找到它。前同事 Gilles Philippart 将这种迁移称为“扼杀您的文档”,这类似于 Martin Fowler 重写部分遗留应用程序的扼杀应用程序模式。

In practice, you would deprecate or remove the old content, possibly with a redirection to the new location for the similar knowledge or an explanation on how to find it from now on. A former colleague, Gilles Philippart, calls such migration “strangling your documentation,” which is similar to Martin Fowler’s strangler application pattern for rewriting parts of legacy applications.

边际文件

Marginal Documentation

您的文档工作不必在第一次尝试时就完成。它应该随着时间的推移而发展。当您愿意改进某些东西时,一种通常是个好主意的方法是专注于边际工作。例如,你可能会说,“从现在开始,每一件新作品都会遵循更高的标准。”

Your documentation endeavor does not have to be complete on the first attempt. It should evolve over time. One approach that’s often a good idea when you’re willing to improve something is to focus on the marginal work. You might say, for example, “From now on, every new piece of work will follow a much higher standard.”

稍微改进你的文档。通过密切关注从现在开始所做的事情,即使遗留代码中仍然重要的部分也会随着时间的推移而得到处理。其余的不用太担心。

Improve your documentation marginally. By paying close attention to what you do from now on, even the parts of the legacy code that still matter will be taken care of over time. And don’t worry too much about the rest.

有时,您可以将新添加的内容隔离在自己干净的泡沫环境中;这使得更容易清楚地满足更高的活文档标准,这只不过是一切的更高标准:命名、代码组织、顶级注释、在代码中可见的清晰而大胆的设计决策,以及更“典型”的设计决策。 ” 活文档,例如动态术语表和图表、强制指南等。

Sometimes you can segregate new additions to live in their own clean bubble context; this makes it easier to clearly meet a higher standard of living documentation, which is nothing but a higher standard of everything: naming, code organization, top-level comments, clear and bold design decisions made visible in the code, and the more “typical” living documentation stuff like a living glossary and diagrams, enforced guidelines, and so on.

案例研究:在批处理系统中引入实时文档

Case Study: Introducing Living Documentation in a Batch System

这个现实世界的示例是关于将信用授权从一个应用程序批量导出到外部系统的。团队成员在任​​时间不足三年,平均,因此这里需要一些文档并没有争议。团队和经理听说了实时文档并且很感兴趣,所以我们最终花了一个小时讨论可以做什么。

This real-world example is about batches to export credit authorizations from one application to external systems. Members of the team stay less than three years, on average, and therefore the need for some documentation is not controversial here. The team and the managers heard about living documentation and are interested, so we eventually spent an hour discussing what could be done.

在考虑要做什么时,我们试图专注于应该记录的所有内容,以改善开发团队的生活。然后,通过查看可用文档的当前状态,我们可以提出更好地管理知识的行动。

When considering what to do, we tried to focus on everything that should be documented in order to improve the life of the development team. Then, by looking at the current state of the available documentation, we could propose actions to better manage the knowledge.

目前,团队成员表示,“有一些文件,但它们已经过时且不可靠。我们通常必须一直询问最有知识的团队成员,以获得执行任何任务所需的知识。”

Currently, team members say, “there are some documents, but they are out of date and not reliable. We usually have to ask the most knowledgeable team member all the time to get the knowledge needed to perform any task.”

这里有很大的改进潜力,包括一些快速的成果。我们可以介绍以下部分中讨论的所有项目,以开始活文档之旅。

There’s a lot of potential for improvement here, including some quick wins. We could introduce all the items discussed in the following sections to start a living documentation journey.

自述文件和现成文档

README and Ready-Made Documentation

源代码存储库的根目录下没有 README 文件。因此,团队可以首先在模块的根目录添加一个 README 文件。

The source code repository does not have a README file at its root. Therefore, the team could first add a README file at the root of the module.

在此自述文件中,团队应明确提及该模块遵循数据泵模式,并对该模式进行简要说明以及指向网络上参考的链接。从实时文档的角度来看,团队将引用现成的文档。

In this README file, the team should mention clearly that this module follows the data pump pattern, with a brief explanation of the pattern and a link to a reference on the web. From a living documentation perspective, the team would be referencing ready-made documentation.

为了更有用,团队可以在 README 文件中详细说明数据泵的主要参数:

To be more useful, the team can elaborate a little bit on the data pump with a description of its main parameters in the README file:

  • 目标系统和格式:使用公司标准 XML 方言。

  • Target system and format: A company-standard XML dialect is used.

  • 治理:此数据泵属于Spartacus Credit Approval组件,并作为其一部分进行管理。

  • Governance: This data pump belongs to the Spartacus Credit Approval component and is managed as part of it.

  • 理由:选择数据泵模式而不是通过服务端点进行更标准的集成,因为目标系统采用批量集成样式,每天在两个系统之间传输大量数据。

  • Rationale: The data pump pattern is chosen over more standard integration through services endpoints because the target system imposes a bulk integration style, with lots of data to transfer between the two systems daily.

所有这些仍然有点抽象,因此最好在自述文件中包含一个指向文件夹的链接,该文件夹包含一些描述组件输入和输出的示例文件:

All this remains a bit abstract, so it’s desirable to include in the README file a link to a folder containing some sample files describing the inputs and outputs of the component:

1 示例输入和输出文件可以在
2 '/samples/' (带有指向 'target/doc/samples' 的链接)
1 Sample input and output files can be found in
2 '/samples/' (with a link to 'target/doc/samples')

商业行为

Business Behavior

该模块的核心复杂性是领域概念资格的确定。它可以通过已在 Cucumber JVM 中部分自动化的业务场景(在名为 eligibility.feature 的功能文件中)来描述。

The core complexity of the module is the determination of the domain concept of eligibility. It is best described by business scenarios that are already partially automated in Cucumber JVM, in a feature file named eligibility.feature.

团队可以重用其中一些场景来生成前面提到的示例文件。这样,示例文件将保持最新。

The team can reuse some of these scenarios to generate the sample files mentioned before. This way, the sample files will remain up-to-date.

拥有业务可读的场景固然很好,但该团队需要让非开发人员可以访问这些场景。基本的 Cucumber 报告可以将场景显示为在线网页。该团队可以考虑使用替代工具 Pickles 来生成实时文档,以便任何人都可以通过搜索引擎以更好的形式在线获取。

Having business-readable scenarios is nice, but this team needs to make these scenarios accessible to nondevelopers. The basic Cucumber report can show the scenarios as a web page online. The team could consider the alternative tool Pickles for the living documentation to be available online to anyone in a better form and with a search engine.

可见的运作和单一的事实来源

Visible Workings and a Single Source of Truth

用于生成 XML 报告的转码在代码和 Excel 文件中定义:

The transcoding used to generate the XML report is defined in code and in an Excel file as well:

1 | 输入字段名称 | 输出字段名称 | 格式化程序|
2 | 交易日期 | 临时日期 | ukToUsDateFormatter | ukToUsDateFormatter |
1 | input field name | output field name | formatter           |
2 | trade date       | TrdDate           | ukToUsDateFormatter |

该团队意识到,这里存在重复的知识并没有什么特别的好处。如果出现分歧,谁是权威?通常它应该是电子表格文件,但过了一段时间,它就会是代码。

The team realizes that there is duplication of knowledge for no particular benefit here. Who’s the authority in case of disagreement? Usually it should be the spreadsheet file, but after a while, it will be the code.

该团队可以通过确定电子表格文件是转码的唯一事实来源(也称为黄金来源)来改善这种情况。然后代码解析该文件并解释它以驱动其行为。在这种方法中,该文件直接是它自己的文档。例如,解析器代码可能如下所示,采用伪代码:

The team could improve that situation by deciding that the spreadsheet file is the single source of truth (aka the golden source) of the transcoding. The code then parses this file and interprets it to drive its behavior. In this approach, the file is directly its own documentation. For example, the parser code could look like this, in pseudo-code:

1 对于数据字典中声明的每个输入字段(例如
XLS 文件)
2 从输入字段中获取值
3 应用格式化程序获取值
4 查找对应的输出字段
5 将格式化值分配给输出字段
1 For each input field declared in a data dictionary (e.g. the
XLS file)
2    Fetch the value from the input field
3    Apply the formatter to obtain the value
4    Lookup the corresponding output field
5    Assign the formatted value to the output field

团队可能会采取相反的方式并确定代码是唯一的事实来源,因此直接从代码生成文件。如果代码主要由大量IF语句组成,则这将不起作用。能够从代码生成可读文件给代码设计带来了通用结构。基本上,代码将嵌入与之前的电子表格文件等效的内容,但硬编码为字典(例如,在 Java 中的地图中)。

The team might go the other way around and decide that the code is the single source of truth and so generate a file directly out of the code. This won’t work if the code is mostly made of a lot of IF statements. Being able to generate a readable file from the code imposes a generic structure to the design of the code. Basically, the code would embed the equivalent of the former spreadsheet file, but hardcoded as a dictionary (for example, in a map in Java).

然后,可以将该数据结构导出为各种格式(.xls、.csv、.xml、.json 等)的文件,供非开发人员使用。

This data structure can then be exported as a file in various formats (.xls, .csv, .xml, .json, and so on) for nondeveloper audiences.

为开发人员提供的综合文档和为其他利益相关者提供的实时术语表

Integrated Documentation for Developers and a Living Glossary for Other Stakeholders

团队真的需要生成 Javadoc 报告吗?在 IDE 中浏览代码非常容易,团队可能不会过多使用 Javadoc 报告。现在,您可以在 IDE 中直接使用 Javadoc 报告。对于类及其类型层次结构的 UML 类图也是如此。所有这些都已经是集成文档,内置于团队编辑器中。

Does the team really need to produce Javadoc reports? It’s so easy to browse the code in the IDE that the team probably won’t use the Javadoc reports much. The Javadoc reports are now available directly at their fingertips in your IDE. The same is true for UML class diagrams of classes and their type hierarchies. All this is already integrated documentation that is built in to the team’s editors.

如果团队确实需要一个参考来让非开发人员访问这些概念,那么它可能会引入一个动态术语表,它会扫描包中的代码,/domain以生成从类中提取的代码中所有业务领域概念的 Markdown 和 HTML 术语表、接口、枚举常量,也许还有一些方法名称和 Javadoc 注释。当然,为了使术语表变得良好,团队可能必须审查并修复许多 Javadoc 注释。

If the team really needs a reference to give access to the concepts to nondevelopers, it might introduce a living glossary, which scans the code in the /domain package to generate a Markdown and HTML glossary of all the business domain concepts in the code, extracted from classes, interfaces, enum constants, and perhaps some method names and Javadoc comments. Of course, for the glossary to be good, the team will probably have to review and fix many of these Javadoc comments.

生动的图表展示设计意图

A Living Diagram to Show the Design Intent

如果内部设计遵循已知的结构,例如六边形架构,团队可以通过相应模块的命名约定使其可见。此命名约定和结构名称必须记录在 README 文件中:

If the internal design follows a known structure, such as the Hexagonal Architecture, the team can make it visible with the naming conventions of the corresponding modules. This naming convention and the name of the structure must be documented in the README file:

1 该模块的设计遵循六边形架构
模式(链接到网络上的参考)。
2
3
4 按照惯例,域模型代码位于
6 src/*/domain*/ 包,剩下的都是基础设施
7 代码
1 The design of this module follow the Hexagonal Architecture
pattern (link to a reference on the web).
2
3
4 By convention, the domain model code is in the
6 src/*/domain*/ package, and the rest is all infrastructure
7 code

这是更现成的文档。

This is more ready-made documentation.

团队可能包含指向域模型包的链接,但它必须能够承受重构更改,例如将域文件夹移动到另一个文件夹中。为了使链接更加稳定,团队可以直接根据命名约定作为正则表达式将其设为书签搜索:src/*/domain*/

The team might include a link to the domain model package, but it has to survive refactoring changes such as moving the domain folder into another folder. To make the link more stable, the team can make it a bookmarked search directly based on the naming convention as a regular expression: src/*/domain*/.

联系信息和导游

Contact Information and Guided Tours

如有疑问应联系谁?按照公司架构师的要求,服务注册中心(本例中为 Consul)应该具有此信息。

Who should someone contact for questions? The service registry, Consul in this case, should have this information, as required by the company architects.

使用自定义注释创建仅针对批次的引导浏览并不是很难,但对于开发人员来说可能不是很有用。该批处理是使用非常标准且文档齐全的 Spring Batch 框架构建的。该框架完全控制处理发生的方式。可以肯定地假设所有开发人员都了解该框架及其工作方式,或者他们可以从标准文档和教程中了解它。无需为此创建额外的自定义导游。

A guided tour just for the batch is not very difficult to create with a custom annotation, but it may not be very useful for developers. The batch is built with the very standard and well-documented Spring Batch framework. This framework completely controls the way the processing takes place. It is safe to assume that all the developers know about this framework and the way it works or that they can learn about it from the standard documentation and tutorials. There is no need to create an additional custom guided tour for that.

微服务大局

Microservices Big Picture

数据泵模块如何适应由许多微服务组成的更大系统?回答这个问题需要付出一些努力。一种方法是定期运行旅程测试(一种经过大量系统组件的端到端场景)在某些启用了分布式跟踪的环境中。可能会想到诸如用于运行测试的 Selenium 和用于分布式跟踪的 Zipkin 等工具。然后,团队可以将分布式跟踪可视化,以生成引导游览,揭示每次旅程测试期间服务之间发生的情况,从而提供系统的整体情况。与活文档一样,管理是从大量细节(服务之间的所有调用以及服务上的所有事件)中过滤出重要内容(例如,在此场景中哪些服务正在与其他服务进行通信)的关键。它们之间的消息传递总线)。

How does the data pump module fit within the bigger system made of many microservices? Answering this question takes some effort. One approach would be to regularly run a journey test (an end-to-end scenario that goes through a large number of components of the system) on some environment with distributed tracing enabled. Tools like Selenium for running the test and Zipkin for distributed tracing may come to mind. The team could then visualize the distributed traces to produce a guided tour that reveals what happens between services during each journey test, to provide a big picture of the system. As is usual for living documents, the curation is key to filtering what matters (for example, what services are talking to what other services in this scenario) out of a huge number of details (all the calls between services and all the events on the messaging bus between them).

向管理层出售活文件

Selling Living Documentation to Management

关于任何新方法的一个常见问题是“我如何说服我的管理层尝试它?” 这个问题根据上下文有不同的答案。

A common question about any new approach is “How do I convince my management to try it?” There are different answers to this question, depending on the context.

第一个也是我更喜欢的答案是,由团队选择满足其他利益相关者期望的方式。要求共享知识是每个人的职责,但团队真的需要批准来决定如何有效地执行其工作吗?请记住,团队中的每个人(开发人员、测试人员和业务分析师)也是项目的利益相关者。为了更好地为其他利益相关者提供服务,他们必须首先照顾好自己。他们还需要足够的自主权来尝试实践,然后,正如伍迪·祖伊尔所说,“扩大好处2 ”,也许会停止那些不可行的事情。

The first, and my preferred, answer is that it is up to the team to select the way to meet the expectations of other stakeholders. It is everyone’s business to require that knowledge be shared, but does the team really need approval to decide how to perform its work efficiently? Keep in mind that everyone on the team—developers, testers, and business analysts—are also stakeholders of the project. To better deliver to other stakeholders, they have to take care of themselves first. They also need enough autonomy to try practices, and then, as Woody Zuill says, “to amplify the good2” and perhaps stop what does not.

2. https://www.infoq.com/news/2016/06/mob-programming-zuill

2.https://www.infoq.com/news/2016/06/mob-programming-zuill

如果您的公司和经理为自己“真正敏捷”并“赋予团队权力”而感到自豪,那么他们应该信任这个团队,并且您不需要任何正式批准就可以尝试实时文档或任何相关的推荐实践,甚至是最激进的,比如结对编程和群体编程。当然,这种自主权伴随着对实际结果的全部责任。

If your company and managers are proud to be “really agile” and to “empower their teams,” then they should trust the team, and you should not need any formal approval to try living documentation or any of the related recommended practices, even the most radical ones, like pair programming and mob programming. Of course, this autonomy comes with the full responsibility for the actual results.

也就是说,第一次建立一个动态术语表或一个动态图表可能需要半天到两天的工作时间。如果没有正式的积压工作,这可能会花费太长的时间,在这种情况下,您需要说服某人。

That said, it may be the case that putting in place a living glossary or a living diagram for the first time requires somewhere between half a day and two days of work. This might be too long to do the effort without having it in a formal backlog, in which case you need to convince someone.

如果有文档预算或者已经计划了文档任务,您可能还想重新利用这些时间来投资于实时文档。同样,这可能需要批准。

If there is a documentation budget or if documentation tasks are planned already, you might also want to reuse that time to invest in living documentation instead. Again, this might require approval.

从实际问题开始

Starting with an Actual Problem

在介绍新方法时,你不应该说教。相反,你应该展示好处,而做到这一点的最佳方法是解决一个准备好解决的实际问题。

When introducing new approaches, you shouldn’t preach. Instead, you should show the benefits, and the best way to do this is on a real problem that is ready to be tackled.

为了找出真正的知识问题,你可能会问周围的人:“有什么事情是你独自一人做的时候觉得不舒服的吗?” 或“您还有什么不清楚的地方吗?” 或者您可能什么也不问,而只是关注白天、一周或迭代期间提出的问题。其中一些会暗示文档的候选者。

To find out a real knowledge problem, you might ask those around you, “Is there anything you would not feel comfortable working on alone?” or “Is there anything that is not clear to you?” Or you might not ask anything but just pay attention to the questions asked during the day, during the week, or during the iteration. Some of them will hint at candidates for documentation.

了解什么是重要的的一种有效方法是仔细记录您在入职期间向每个新人提到或解释的所有内容。如果您向新加入者索要一份惊讶报告,它将包含应修复或记录的内容的候选内容。

One efficient way to know what’s important is to carefully take notes on everything you mention or explain to each newcomer during the onboarding period. If you ask new joiners for an astonishment report, it will contain candidates for stuff that should be either fixed or documented.

如果您发现了知识共享问题,请确保每个人都承认这是一个真正值得解决的文档问题。然后受本书启发提出一个解决方案。您不必使用“活文档”这个术语,但只需提及您知道其他公司、大型公司以及小型早期初创公司已经采用的方法即可。

If you have identified a knowledge-sharing issue, make sure everyone acknowledges that it is a genuine documentation problem worth tackling. Then propose a solution, inspired from this book. You don’t have to use the term living documentation but can just mention that you know of an approach that has already been done in other companies, in large-ish corporations, and in small early startups, too.

您也可以从一些小事情开始,用自己的时间完成,然后向您想要说服的经理展示。它可以是一份报告、一张图表,或者是文档加上一些经理特别感兴趣的指标的组合。强调如何通过使用该方法来节省时间并提高满意度。

You may also start with something small, done on your own time, that you can show to the managers you want to convince. It could be a report, or a diagram, or a mix of a documentation plus some indicators that managers are particularly interested in. Emphasize how you can save time and improve satisfaction by using to the approach.

一旦你的项目完成,其好处应该足以说服人们保留这种方法。如果没有好处,请告诉我,以便我改进这本书。尽管如此,即使在最坏的情况下,你也会学到一些有价值的东西在此过程中,您可能会得到一个传统文档的示例,该示例仅比典型文档贵一点。

Once your project is done, the benefits should be enough to convince people to keep the approach. And if the benefits are not there, please tell me, so that I can improve this book. Still, even in the worst case, you will learn something valuable in the process, and you will probably have one example of traditional documentation that was just a bit more expensive than typical.

活生生的文献记录倡议

A Living Documentation Initiative

如果有很多紧迫的文档问题,您可能需要从一个关于活文档的雄心勃勃的计划开始。本书可以帮助你推动这个想法,并使其成为一个可以作为参考的标准包。把这本书展示给你想说服的人看。展示同一主题的演讲视频(例如,参见https://vimeo.com/131660202)。我制作了很多这样的视频,都受到了好评。

If there are a lot of pressing documentation issues, you might want to start with a somewhat ambitious initiative on living documentation. This book can help you push the idea forward and make it a standard package that can be a reference. Show the book to the people you want to convince. Show a video of talks on the same topic (see, for example, https://vimeo.com/131660202). I’ve done many such videos that have been well received.

在公司内展示“试点”案例的好处通常是最好的选择。一开始没有人会过多关注,但随着早期的成功,更多的人会为了自己的职业利益而尝试复制甚至正式化这一举措。

Showing the benefits on a “pilot” case within a company is usually the best option to start with. Nobody will pay much attention at first, but with early successes, more people will try to replicate or even formalize the initiative for the benefit of their careers.

一旦我们谈论一项已确定的计划,我们就必须说服高层管理人员,值得在团队上投入时间,以及可能进行一些额外的指导和咨询。销售活文档的一种方法是考虑到它是实现可持续持续交付的先决条件,有点像测试也是一个先决条件:就像您需要一个自动化测试策略来快速进行一样,您也需要一个活文档策略。

As soon as we’re talking about an identified initiative, we have to convince upper management that it is worth investing time in the teams, as well as possibly some additional coaching and consulting. One way to sell living documentation is to consider that it is a prerequisite to achieve sustainable continuous delivery, and a bit like testing is a prerequisite, too: Just as you need an automated testing strategy to go fast, you also need a living documentation strategy.

采用活文档方法的许多关键原因已经在您眼前,并显示在您每周的时间跟踪和知识管理的当前状态中。

Many of the key reasons to adopt a living documentation approach are already in front of your eyes and show up in your weekly time-tracking and in the current state of your knowledge management.

总体而言,我的感觉是文档是管理者最关心的问题。团队成员之间的技能和知识传递问题已经成为管理层普遍焦虑的根源;它代表着时间成本,更重要的是,缺陷和错误的成本:

Overall, my feeling is that documentation is a concern close to managers’ hearts. The matter of skills and knowledge transmission between team members is already a common source of anxiety for management; it represents a cost in time and also, more importantly, in defects and mistakes:

  • 技能矩阵创建和更新

  • Skills matrix creation and updates

  • 周转率

  • Turnover rate

  • 新人入职所花费的时间

  • Time spent onboarding newcomers

  • 与卡车因素相关的焦虑(如果团队在午餐回来时被卡车撞到,则存在失去关键知识的风险)

  • Anxiety related to the truck factor (the risk of losing key knowledge if a team is hit by a truck when coming back from lunch)

  • 由“我不知道”引起的缺陷和事件的比例

  • The ratio of defects and incidents caused by “I didn’t know that”

缺乏文档是一种隐性成本,就像缺乏测试一样。每一个改变都需要完整的调查和评估,有时甚至是预研究。每次都必须重新挖掘隐藏的知识。或者,将以不符合系统先前愿景的方式进行更改,这使得应用程序变得越来越臃肿,并且随着时间的推移,情况会变得更糟。这可能会显示在以下内容中

Lack of documentation is a hidden cost, just like the lack of tests. Every change needs a complete investigation and an assessment, sometimes even a pre-study. The hidden knowledge has to be mined again each time. Alternatively, changes will be made in a way that is not in line with the previous vision of the system, which makes the application increasingly bloated and makes matters worse over time. This may show up in the following as

  • 增加了交付变更的时间。

  • Increased time to deliver a change.

  • 任何代码质量指标的负面趋势,其中最能说明问题的是代码库的大小。(如果它有规律地增长,很可能是设计太弱的迹象。没有足够的重构开始,每次更改都是添加。)

  • Negative trends of any code quality metrics, the most telling of which being the mere size of the code base. (If it grows regularly, it is probably a sign that the design is too weak. There is not enough refactoring to start with, and each change is an addition.)

关于文档本身也存在争议,或者缺乏文档:

And there are also arguments on the documentation, or lack thereof, in itself:

  • 未完成的文档任务,或文档明显更新得不够频繁

  • Documentation tasks that are not done, or documentation that is visibly not updated frequently enough

  • 有关文件的合规要求

  • Compliance requirements with respect to documentation

  • 编写文档或更新现有文档所花费的时间

  • Time spent writing documentation or updating existing documentation

  • 寻找正确文档所花费的时间

  • Time lost searching the right documentation

  • 阅读不正确的文档浪费了时间

  • Time lost reading documentation that is incorrect

您可能想要对冒充文档的现有文档进行质量审查,重点关注各种指标,例如:

You might want to perform a review of the quality of the existing documents that pretend to be the documentation, with a focus on various indicators, such as the following:

  • 可以找到文档的不同位置的数量(包括源代码、wiki、每个共享驱动器和团队成员的计算机)

  • The number of different places where documentation can be found (including the source code, the wiki, each shared drive, and team members’ machines)

  • 上次更新时间

  • The time of last update

  • 最后更新的作者离开团队的比例

  • The proportion of authors of the last updates who left the team

  • 文档中的基本原理数量(解释原因而不只是解释内容)

  • The amount of rationale (explaining why rather than just what) in the documentation

  • 仍然可信的页数、段落数或图表数

  • The number of pages or paragraphs or diagram that can still be trusted

  • 来源和另一种文档之间冗余的知识

  • The among of knowledge that is redundant between the source and another kind of documentation

  • 一个简短的调查,例如“你知道我在哪里可以找到这方面的知识吗?” 关于一组随机的关注点

  • A short survey such as “Do you know where I can find knowledge on that?” on a random set of concerns

您可以提出许多其他想法来帮助实现文档的实际状态。如果一切都很好并且在控制之下,那么活文档唯一可以改善的是长期成本,这要归功于团队成员更多的合作、自动化以及各种类型浪费的减少。

You can come up with many other ideas to help realize the actual state of documentation. If everything is fine and under control, then the only thing that living documentation might improve is the long-term cost, thanks to team members working together more, automation, and reduction of various types of waste.

如果一切都不且没有得到控制,活文档可以使文档再次变得可行,成本合理,并具有确定的附加值。

If everything is not fine and under control, living documentation can make documentation feasible again, at a reasonable cost and with identified value added.

从价值的角度来看,值得强调最大的好处,这不仅仅是知识的共享,而且尤其是在此过程中改进软件的附带好处(请参阅第 11 章,“超越文档:生动的设计”

On the value side, it is worth putting the emphasis on the biggest benefits, which are not just the sharing of knowledge but especially the side benefits of improving the software in the process (see Chapter 11, “Beyond Documentation: Living Design”).

将现状与美好世界的承诺进行对比,以满足人们的愿望

Contrasting the Current Situation with the Promise of a Better World to Match People’s Aspirations

南希·杜阿尔特 (Nancy Duarte)在她的《共鸣》一书中提出了如何通过演示激发兴奋和热情的建议。首先要知道为什么你想改变事情。如果您决定向您的团队或公司引入活文档,您可以首先回答以下问题:“为什么我想分享和推广它?” 和“我为什么兴奋?”

In her book Resonate, Nancy Duarte offers suggestions on how to stimulate excitement and enthusiasm through presentations. It starts with knowing why you want to change things. If you’ve decided to introduce living documentation to your team or company, you could start by answering these questions: “Why do I want to share and promote that?” and “Why am I excited?”

然后您可以将当前情况与您想要推广的新实践进行对比。以下是常见的挫败感示例,可以与活文档方法的好处进行对比:

Then you can contrast the current situation with the new practice you’d like to promote. Here are examples of common frustrations that could be contrasted with the benefits of a living documentation approach:

  • 你不写文档,你会为此感到内疚。

  • You don’t write documentation, and you feel guilty about that.

  • 向团队成员、新加入者和团队外的利益相关者解释事情需要持续花费大量时间。

  • Explaining things to team members, new joiners, and stakeholders outside the team takes a lot of time on an ongoing basis.

  • 您编写文档,但您更喜欢编写代码。

  • You write documentation, but you’d prefer to write code.

  • 您正在寻找文档,当您找到一些文档时,您不能信任它,因为它已经过时了。

  • You’re looking for documentation, and when you find some, you cannot trust it because it’s out of date.

  • 当您创建图表时,您会因为花费如此多的时间而感到沮丧。

  • When you create diagrams, you’re frustrated that it takes so much time.

  • 寻找合适的文档需要花费大量的时间,而获得的好处却很小,以至于您常常会放弃并尝试在没有它的情况下完成工作。

  • Looking for the right document takes so much time for such a small benefit that you often give up and try to do the work without it.

  • 当您通过大量对话进行敏捷协作时,您会感到不舒服,因为您的组织希望提供更多可追踪和存档的文档。

  • When you collaborate the agile, way with lots of conversations, you feel uncomfortable because your organization expects to deliver more traceable and archived documents.

  • 您手动完成许多繁琐的工作,包括部署、向外部人员解释内容以及文书工作,并且您感觉这些工作是可以避免的。

  • You do a lot of tedious work manually, including deployment, explaining stuff to external people, and paperwork, and you have a feeling it could be avoided.

当然,您可以自定义并决定哪些项目对您的环境影响最大,并决定实时文档的哪一部分最能缓解这种挫败感。

Of course, it’s up to you to customize and decide which items have the most impact in your context and to decide what part of living documentation remedies that frustration most.

以下概括对于开发人员来说往往是正确的:

The following generalizations tend to be true of developers:

  • 他们不喜欢写文档。

  • They don’t like writing documentation.

  • 他们喜欢写代码。

  • They like to write code.

  • 他们热爱代码,并发现用代码做更多事情很有吸引力。

  • They love code and find that doing more with code is appealing.

  • 他们讨厌手动的、重复性的任务。

  • They hate manual, repetitive tasks.

  • 他们喜欢自动化。

  • They love automation.

  • 他们为美丽的代码感到自豪。

  • They are proud of beautiful code.

  • 他们喜欢纯文本和他们最喜欢的工具。

  • They love plain text and their favorite tools.

  • 他们喜欢逻辑性的东西(例如,文本优先、DRY)。

  • They love logical things (for example, text-first, DRY).

  • 他们喜欢展示技艺和极客文化。

  • They love to exhibit mastery and geek culture.

  • 他们希望技能得到认可。

  • They want recognition of skills.

  • 他们对现实生活中的混乱情况表示同情。

  • They empathize with real-life messy situations.

另一方面,以下概括往往适用于管理者:

On the other hand, the following generalizations tend to be true of managers:

  • 他们喜欢团队工作更加透明。

  • They love more transparency on the work of their teams.

  • 他们喜欢看到事物以他们能感觉到的方式呈现,以便他们能够了解事情是变得更好还是更糟。

  • They love to see things presented in ways they can feel so that they can understand whether it’s getting better or worse.

  • 他们喜欢可以自己向其他人展示并引以为豪的文档。

  • They like documentation they can themselves show someone else and be proud of.

  • 他们希望文档更加防流转。

  • They want documentation to be more turnover-proof.

看到双方的情况很重要。对于文档策略来说,展现每个人都真正希望实现的愿景至关重要。

It’s important to see both sides. It’s critical for a documentation strategy to exhibit a vision that everybody would genuinely like to happen.

精神上的顺从

Compliance in Spirit

通过以精神而非文字为目标,活文档方法甚至可以满足最苛刻的合规要求。

A living documentation approach can work with even the most demanding compliance requirements by aiming for the spirit instead of aiming for the letter.

如果您的域名受到监管,或者您的公司出于合规原因需要大量文档(例如 ITIL),您可能会花费大量时间文档任务。来自活文档的想法可以满足合规性目标,减轻团队的负担并节省时间,同时提高生成的文档和产品的质量。

If your domain is regulated or if your company requires a lot of documentation for compliance reasons (for example, ITIL), you probably spend a lot of time on documentation tasks. The ideas from living documentation can meet the compliance goals, reducing the burden for the teams and saving time, while improving the quality of the produced documentation and of the product at the same time.

监管机构通常关注需求跟踪和变更管理,以此作为提高质量的一种方式。例如,美国食品和药物管理局在其《软件验证的一般原则;行业和 FDA 工作人员最终指南”:

Regulators often focus on requirements tracking and change management as a way to improve quality. For example, the U.S. Food and Drug Administration writes in its “General Principles of Software Validation; Final Guidance for Industry and FDA Staff”:

软件代码中看似微不足道的更改可能会在软件程序的其他地方产生意想不到的非常严重的问题。软件开发过程应该经过充分的规划、控制和记录,以检测和纠正软件变更带来的意外结果。

Seemingly insignificant changes in software code can create unexpected and very significant problems elsewhere in the software program. The software development process should be sufficiently well planned, controlled, and documented to detect and correct unexpected results from software changes.

鉴于对软件专业人员的高需求和高度流动的劳动力,对软件进行维护更改的软件人员可能没有参与原始软件的开发。因此,准确而彻底的文档记录至关重要。3

Given the high demand for software professionals and the highly mobile workforce, the software personnel who make maintenance changes to software may not have been involved in the original software development. Therefore, accurate and thorough documentation is essential.3

3.美国食品和药物管理局,《软件验证的一般原则》;行业和 FDA 工作人员最终指南”, http://www.fda.gov/RegulatoryInformation/Guidances/ucm085281.htm

3.U.S. Food and Drug Administration, “General Principles of Software Validation; Final Guidance for Industry and FDA Staff,” http://www.fda.gov/RegulatoryInformation/Guidances/ucm085281.htm

同一份 FDA 文件还描述了测试以及设计和代码审查的重要性。

The same FDA document also describes the importance of testing and of design and code reviews.

乍一看,敏捷实践似乎不太注重文档,因此不太适合苛刻的合规性要求。但事实恰恰相反。当应用属于活文档范围的敏捷实践时,您实际上拥有的文档流程比所有传统的大量文档流程更为严格。

It might look at first glance as though agile practices are less documentation oriented and therefore are not well suited for demanding compliance requirements. But quite the opposite is true. When agile practices, which are part of the living documentation spectrum, are applied, what you actually have is a documentation process that is more rigorous than all the traditional documentation-heavy processes.

示例规范 (BDD) — 包含自动化场景、实时图表和实时术语表 — 为每个构建提供广泛的文档。如果您在一小时内提交五次,那么您的文档每小时更新五次,并且始终准确。纸张密集型流程无法想象这种性能水平!

Specification by example (BDD)—with scenarios with automation, living diagrams, and a living glossary—provides extensive documentation on each build. If you commit five times in an hour, you get your documentation updated five times per hour, and it is always accurate. Paper-heavy processes cannot dream of this level of performance!

与同事共同努力,确保至少三到四个人了解每个变更,这也是对各种合规性要求的重要贡献,即使这些知识不一定是写在源代码之外的。

Working collectively with colleagues to ensure that at least three or four people know of each change is also an important contribution to various compliance requirements, even though the knowledge is not necessarily written outside the source code.

您可以在这里看到这个想法:一个精通敏捷开发实践和原则(包括实时文档和其他持续交付想法)的开发团队已经非常接近满足大多数合规性要求,甚至是众所周知的 ITIL 等繁重要求。

You see the idea here: A development teams with a good command of agile development practices and principles, including living documentation and other continuous delivery ideas, is already quite close to matching most compliance requirements, even the notoriously heavy ones like ITIL.

请记住,敏捷实践通常不一定符合公司合规指南的实施细节,这些指南通常充满了繁琐的程序和文书工作。尽管如此,敏捷实践通常可以达到甚至超过合规机构所设定的更高级别的目标,这些目标围绕风险缓解和可追溯性。无论敏捷与否,在开发团队或合规办公室中,我们都希望降低风险、一定程度的可追溯性、质量受控以及一切方面的改进。您不必遵循 2,000 页枯燥的 ITIL 指南。您可以替代更有效的替代实践,并且仍然能够选中高级目标清单中的大多数复选框。

Keep in mind that agile practices in general do not necessarily meet the implementation details of your company compliance guidelines, which are often full of burdensome procedures and paperwork. Still, agile practices often meet or even exceed the higher-level goals aimed for by the compliance bodies, which revolve around risk mitigation and traceability. Agile or not, in the development team or in the compliance office, we all want risk mitigation, some reasonable amount of traceability, quality under control, and improvement in everything. You don’t have to follow 2,000 pages of boring ITIL guidelines. You can substitute alternative practices that are more efficient and still be able to check most checkboxes in the checklist of the high-level objectives.

因此:审查合规性文档要求,并针对每个项目,确定如何通过活文档方法来满足它,通常通过使用轻量级声明、知识增强和自动化。基于公司模板的强制性正式文档可以很容易地从以完全不同的方式管理的知识中生成(例如,从源代码控制系统、代码和测试)。当合规性期望过于繁重时,请回到更高级别的目标,并确定如何通过您的实践直接满足该目标。每当存在真正的差距时,这就是改进开发过程的机会。最后,确保合规团队不时审查您的轻量级流程,以便为您的团队授予永久的预批准印章。

Therefore: Review the compliance documentation requirements, and for each item, identify how it could be satisfied with a living documentation approach, typically by using lightweight declarations, knowledge augmentation, and automation. Mandatory formal documents based on company templates can easily be generated from knowledge managed in a totally different fashion (for example, from the source control system, the code, and the tests). When the compliance expectations are too burdensome, go back to their higher-level goal and identify how this goal could be directly satisfied with your practices instead. Whenever there is a real gap, it’s an opportunity to improve your development process. Finally, make sure your lightweight process is reviewed from time to time by the compliance team, so that it can grant your team a permanent preapproval stamp.

您会惊讶地发现您的实时文档能够满足或超出合规性期望。

You’ll be surprised how your living documentation can meet or exceed compliance expectations.

案例研究:遵守 ITIL

Case Study: Compliance with ITIL

Paul Reeves 在他的精彩博客文章“Agile vs. ITIL”中说道:

Paul Reeves says in his great blog post “Agile vs. ITIL”:

人们常常认为快速部署/持续部署/每日构建等无法在高度面向流程的环境中工作,在该环境中必须遵循规则和流程。(通常他们只是不喜欢别人的规则。)

Often people believe that rapid deployment / continuous deployment / daily builds etc. can’t work in an environment that is highly process oriented, where rules and process have to be followed. (Usually they just don’t like someone else’s rules.)

嗯,这个流程是为了确保一致性、责任、问责、沟通、可追溯性等,当然它可以被设计成一个障碍。或者,可以将其设计为允许快速发布版本。人们指责流程或 ITIL 只是不成熟。他们也可能归咎于天气。4

Well, the process is there to ensure consistency, responsibility, accountability, communication, traceability, etc. and of course it CAN be designed to be a hinderance. It, alternatively, CAN be designed to allow quick passage of releases. People blaming process or ITIL are just being immature. They may as well blame the weather.4

4. Paul Reeves,Reeves 的结果博客, http://reevesresults.blogspot.fr/2011/03/agile-vs-itil.html

4.Paul Reeves, Reeves’s Results blog, http://reevesresults.blogspot.fr/2011/03/agile-vs-itil.html

我应用持续交付理念的经验确实表明,可以从开发团队内部的轻量级、敏捷、低周期时间流程映射到更传统、通常较慢且纸张密集型的流程外部。与普遍看法相反,您的敏捷流程可能比以 ITIL 按书本方式管理的其他项目更有纪律:很难击败自动化可以生成大量功能文档、大量测试结果和覆盖范围、安全性的流程。以及可访问性检查、设计图表和发行说明,其中包含工具中所需功能的链接以及用于发布决策的存档电子邮件,针对每个版本,每天多次!

My experience from applying the ideas of continuous delivery has shown indeed that it is possible to map from a lightweight, agile, low-cycle-time process inside the development team to a more traditional, usually slower and paper-intensive process outside. In contrast to common beliefs, your agile process is probably more disciplined than the other project managed in an ITIL-by-the-book fashion: It’s hard to beat a process where automation can produce extensive functional documentation, extensive test results and coverage, security and accessibility checks, design diagrams, and release notes with links to the requested features in a tool and archived emails for the release decision, on each build, several times a day!

当严格的程序很重要时,使用自动化和强制指南是确保它们得到尊重的最佳方式,同时减少手动应用它们的负担。程序对于机器来说非常有用,但对于人来说则不然。正确的工具可以保护开发团队,同时消除手动杂务。然而,这似乎是一个悖论,好的工具仍然会在质量期望未得到满足时将其变得非常明显,从而引起人们对质量期望的关注。有了这个保护带,每个团队成员都可以了解工作中的质量期望,同时获得始终高效工作的满足感。

When strict procedures are important, using automation and enforced guidelines is the best way to make sure they are respected while reducing the burden of manually applying them. Procedures are great for machines, not for people. The right tools protect a development team and remove the manual chores at the same time. However, and it may seem like a paradox, good tools still draw attention to the quality expectations by making very visible whenever they are not met. With this protective harness, every team member is learning the quality expectations on the job, while having the satisfaction of always doing productive work.

ITIL 示例

The ITIL Example

让我们通过查看表 13.1表 13.2重点关注在 ITIL 概念框架下管理变更请求的示例。

Let’s focus on an example of managing requests for change under the ITIL conceptual framework by looking at Table 13.1 and Table 13.2.

表 13-1 变更管理请求

Table 13-1 Request for Change Management

改变活动

Change Activity

敏捷被遗忘的实践示例

Example of Agile Forgotten Practice

文档媒体示例

Example of Documentation Media

变更请求收集

Collection of change requests

用户故事或错误以及增强功能,包括描述、来源、请求者、日期、业务优先级和预期收益

User stories or bugs and enhancements with description, origin, requestor, date, business priority, and expected benefits

墙上的贴纸和跟踪工具(例如 Jira)

Stickers on the wall and a tracking tool (such as Jira)

研究和影响

Study and impacts

BDD、TDD、测试

BDD, TDD, tests

所有活的文档工件

All living documentation artifacts

决定

Decisions

决定、决策者姓名、目标版本、日期

Decision, names of decision makers, target version, date

CAB 报告(通过电子邮件发送 PDF 格式)

CAB report (email as PDF)

跟进

Follow up

未开始、进行中、完成),受让人

Not started, in progress, done), assignee

跟踪工具(如 Jira)

Tracking tool (such as Jira)

表 13-2 发布管理

Table 13-2 Release Management

发布活动

Release Activity

敏捷实践示例

Example of Agile Practice

文档媒体示例

Example of Documentation Media

内容

Content

发行说明,包含相关变更、日期、停机时间、测试策略、影响(业务、IT、基础设施、安全)的链接

Release notes with a link to related change(s), dates, downtime, test strategy, impacts (business, IT, infrastructure, security)

票务工具(可以自动作为预先编写的文档和生成的发行说明的组合)

Ticketing tool (may be automated as a mix of prewritten documents and generated release notes)

影响

Impacts

基于变更研究以及迭代演示的反馈

Based on the change study plus the feedbacks from the iteration demo

实时文档,存档为 PDF

Living documentation, archived as PDF

发布检查

Release checks

自动化测试,包括SLA测试、预生产环境中的部署测试、冒烟测试

Automated test, including tests on SLA, deployments tests in pre-production environments, smoke tests

CI 工具、部署工具结果、测试报告

CI tool, deploy tool results, tests reports

赞同

Approval

决策、决策者姓名、实际交付日期、目标版本、推出日期、决策日期、通过/不通过条件

Decision, names of the decision makers, actual delivery date, target version, rollout date, decision date, go/no-go conditions

电子邮件另存为 PDF

Email saved as PDF

部署成功

Deployment successful

部署和部署后测试

Deployment and post-deployment tests

部署工具和部署后测试报告

Deployment tool and post-deployment test reports

连续的提高

Continuous improvement

回顾记录,包括名称、行动计划、问题

Retrospectives notes, with names, action plan, issues

维基、电子邮件、白板图片

Wiki, email, picture of the whiteboard

请注意,敏捷实践促进尽可能细化工作。当迭代包含数十个切片且每个切片只有几个小时时,在跟踪工具中跟踪每个切片很不方便。但这种粒度级别对于变更请求的管理来说并不重要。因此,您只能在工具中跟踪切片的内聚聚合。

Note that agile practices promote slicing the work as thinly as possible. It is inconvenient to track every slice in a tracking tool when an iteration contains dozens of slices, each only a few hours long. But this level of granularity does not matter much for the management of requests for change; as a consequence, you may only track cohesive aggregates of slices in the tool.

这里的重点是要认识到您的实时文档可以满足或超过最严格的合规性期望,同时将额外的合规性特定工作保持在最低限度。如果您处于合规性密集型环境中,这本身就可能是引入活文档的动力。

The point here is really to realize that your living documentation can meet or exceed the toughest compliance expectations while keeping the extra compliance-specific work to a minimum. This could be an incentive in itself to introduce living documentation if you’re in a compliance-intensive environment.

概括

Summary

引入活文档最好先秘密进行,以建立信心以扩展到更大、更明显的计划。作为开始,您可以决定

Introducing living documentation is best done undercover first, to build the confidence to expand to bigger and more visible initiatives. As a start, you can decide to

使用本书中讨论的一些模式,将痛苦的传统文档迁移到更活文档中。

migrate painful traditional documentation into its more living equivalent, using some of the patterns discussed in this book.

如果您需要预算或时间来扩大工作范围,请记住,管理者通常关心保留知识。当有人反对你的情况特殊,因为你受到监管或者你必须遵循一些严格的合规框架时,请记住,通过坚持精神而不是文字,你可以满足甚至超越要求。

If you need budget or time to expand your efforts, remember that managers are often concerned about retaining knowledge. And when facing the objection that your situation is special because you’re regulated or you have to follow some strict compliance framework, remember that you can meet and even exceed requirements by sticking to the spirit instead of the letter.

第14章

Chapter 14

记录遗留应用程序

Documenting Legacy Applications

宇宙是由信息组成的,但它没有意义——意义是我们的创造。寻找意义就是在镜子里寻找。

The universe is made of information, but it doesn’t have meaning - meaning is our creation. Searches for meaning are searches in a mirror.

—@KevlinHenney

—@KevlinHenney

这句话说明了遗留系统的情况:它们充满了知识,但通常是加密的,而我们丢失了密钥。如果没有测试,我们就无法明确定义遗留系统的预期行为。如果没有一致的结构,我们就必须猜测它是如何设计的、出于什么原因以及它应该如何演变。如果没有仔细的命名,我们就必须猜测和推断变量、方法和类的含义,以及哪些代码负责什么。

This quote illustrates the case with legacy systems: They are full of knowledge, but it is usually encrypted, and we have lost the keys. Without tests, we have no clear definition of the expected behavior of a legacy system. Without consistent structure, we have to guess how it was designed and for what reasons and how it is supposed to be evolved. Without careful naming, we have to guess and infer the meaning of variables, methods, and classes, as well as what code is responsible for what.

简而言之,当系统的知识不易获取时,我们将系统称为“遗留系统”。它们体现了我们所说的“文档破产”。

In short, we call systems “legacy” when their knowledge is not readily accessible. They exemplify what we could call “documentation bankruptcy.”

文件破产

Documentation Bankruptcy

遗留应用程序非常有价值,并且不能简单地拔掉它们。大多数完全重写大型遗留系统的尝试最终都会失败。遗留系统是富裕组织的一个问题,这些组织生存并盈利的时间足够长,足以发展遗产。

Legacy applications are quite valuable, and they cannot be simply unplugged. Most attempts to completely rewrite large legacy systems eventually fail. Legacy systems are a problem of rich organizations, which lived and became profitable long enough to grow a legacy.

不过,当遗留应用程序由于环境变化而必须发展时,就会出现问题,因为更改它们通常成本高昂。高昂的变革成本与许多问题有关,包括重复和缺乏自动化测试,以及知识的丢失。任何更改都需要对代码库中的知识进行漫长而痛苦的逆向工程,包括大量猜测,然后才能最终触及一行代码。

Still, legacy applications raise issues when they have to evolve due to changing context because changing them is usually expensive. The prohibitive cost of change is related to many issues, including duplication and lack of automated testing, and also to the lost knowledge. Any change requires a long and painful reverse engineering of the knowledge from the code base, including a lot of guesswork, before one line of code is eventually touched at the end.

不过,一切并没有失去。在接下来的几页中,您将看到一些实时文档技术,这些技术特别适用于在遗留系统中工作或从遗留系统工作时。

All is not lost, though. In the following pages, you’ll see a few living documentation techniques that particularly apply when working in or from legacy systems.

遗留应用程序成为僵化的知识

Legacy Application as Fossilized Knowledge

您之前已经知道,任何可以回答问题的东西都可以被视为文档。如果您可以使用应用程序回答问题,那么该应用程序就是文档的一部分。这对于规范丢失的遗留系统来说非常重要。你必须使用它才能知道它的行为方式。

You’ve seen before that anything that can answer a question can be considered documentation. If you can answer questions by using an application, then the application is part of the documentation. This is of great relevance in the case of a legacy system with lost specifications. You have to use it to know how it behaves.

在重写部分遗留系统的情况下,将遗留系统视为知识源可能很方便,因为新系统可能会继承其前身的大部分行为。对于新系统中的每项功能,规范都可以借鉴以前的系统。在实践中,在参加规范研讨会时,您可以检查旧应用程序的行为方式,以获得新应用程序的灵感。

In the context of rewriting part of a legacy system, considering the legacy system as a source of knowledge can be handy since the new system will probably inherit a significant part of the behaviors of its predecessor. For each feature that will make it into the new system, the specifications can draw on the former system. In practice, while doing the specifications workshops, you can check how the legacy application behaved to get inspiration for the new one.

“用相同规格重写”的谬论

The “Rewriting with the Same Specs” Fallacy

重写遗留系统的一种常见失败模式是使用完全相同的规范来重写它们。在不改变任何东西的情况下重写系统是没有意义的;这样做只会带来很大的风险和很大的浪费。仅更改技术堆栈也几乎没有什么好主意,除非您的硬件不再在商业上可用并且没有模拟器。

One common failure mode of rewriting legacy systems is to rewrite them with exactly the same specifications. It makes little sense to rewrite a system without changing anything; doing so is just a lot of risk and a lot of waste. Changing only the technology stack hardly does any good idea either, unless your hardware is no longer available commercially and there is no emulator.

即使从纯粹的技术角度来看,重写软件也是一项昂贵的工作,而提高回报的最佳方法是利用机会同时重新考虑规范。许多功能不再有用。许多功能应该适应新的用途和环境。UI 及其 UX 必须发生巨大变化,并且这些变化将对底层服务产生影响。您还希望新应用程序能够更便宜、更频繁地交付,因此您也需要自动化测试,当您按照 BDD 的建议从明确的规范作为具体示例开始时,自动化测试会更便宜。

Rewriting a piece of software is an expensive endeavor, even from a purely technical perspective, and the best way to improve the return is by taking the opportunity to reconsider the specifications at the same time. Many features are no longer useful. Many features should be adapted to new usages and contexts. The UI and its UX have to change drastically, and the changes will have impacts on the underlying services. You’ll also want the new application to be cheaper to deliver more frequently, so you’ll want automated testing, too, which comes cheaper when you start from clear specifications as concrete examples, as advised by BDD.

我强烈建议不要用相同的规格重写。重写系统的有限部分,并将其视为一个从头开始的项目,将遗留系统、工作应用程序及其源代码作为奖励,从中汲取灵感。

I strongly suggest not rewriting with the same specs. Rewrite a limited portion of the system and consider it as a project from scratch, with the legacy system, the working application, and its source code as a bonus to draw inspiration from.

因此:在重写遗留系统的一部分的情况下,请将遗留系统视为文档,以补充有关规格,而不是给定的规格。确保业务人员(例如领域专家、业务分析师或产品负责人)与团队密切合作。不要误以为遗留系统本身就足以描述要重建的新系统。利用重写的机会来挑战遗留系统的各个方面:功能范围、业务行为、模块的构建方式等等。从一开始就重新控制知识,并通过具体场景和清晰设计来表达清晰的规范。

Therefore: In the context a rewriting a part of a legacy system, consider the legacy system as documentation to complement the discussions on the specifications, not as the given specifications. Make sure a business person such as a domain expert, business analyst, or product owner works closely with the team. Don’t fall for the fallacy that the legacy system is in itself a sufficient description of the new system to be rebuilt. Take the opportunity of the rewriting to challenge every aspect from the legacy system: the functional scope, the business behaviors, the way it is structured into modules, and so on. Regain control of the knowledge from the start, with clear specifications expressed as concrete scenarios and a clear design.

理想的配置是一个完整的团队,拥有团队内部的所有技能和角色,使用三个朋友的思想:业务视角、开发视角和质量视角。

The ideal configuration is a whole team, with all skills and roles inside the team, using the idea of the three amigos: business perspective, development perspective, and quality perspective.

与纯粹从头开始的项目相比,能够访问正在运行的遗留应用程序及其源代码是一个不错的好处。这就像团队中多了一位专家,即使他是一位老专家,有时甚至是无关紧要的专家。毕竟,遗留系统是许多不同的人在很长一段时间内做出的决策拼凑而成的结果。这是一块化石。

Having access to both a working legacy application and its source code is a nice bonus compared to projects starting purely from scratch. It’s like having another expert on the team, even if it is an old, sometimes irrelevant, expert. After all, the legacy system is the result of a patchwork of the decisions of many different people over a long period of time. It’s a fossil.

何时可以对遗留系统进行检测的一个完美示例是,它可以提供“此功能多久使用一次?”这个问题的答案。

A perfect example of when a legacy system can be instrumented is when it can provide the answer to the question “How often is this feature used?”

考古学

Archeology

软件源代码是我们拥有的最密集的通信形式之一。但是,它仍然是人类交流的一种形式。重构为我们提供了一个非常强大的工具,可以帮助我们更好地理解别人所写的内容。

Software source code is one of the most densely packed forms of communication we have. But, it is still a form of human communication. Refactoring gives us a very powerful tool for improving our understanding of what someone else has written.

—Chet Hendrickson,《软件考古学:了解大型系统》

—Chet Hendrickson, Software Archeology: Understanding Large Systems

当您询问遗留代码库的问题时,您需要一张纸和一支笔始终靠近键盘来记笔记和画画。您需要为手头的任务创建地形的按需地图。在探索代码并在运行时或使用调试器进行操作时,您需要写下输入、输出以及您发现的所有影响。您需要记下所读或所写的内容,因为副作用才是最重要的。了解这些信息对于模拟或估计变更的影响也至关重要。您应该概述每个职责如何依赖于其邻居,迈克尔·费瑟斯(Michael Feathers)在他的著作《有效处理遗留代码》中将这种技术称为效果图1

When you ask questions of a legacy code base, you need a piece of paper and a pen close to your keyboard at all times to take notes and draw. You need to create an ondemand map of the terrain for the task at hand. While exploring the code and playing with it at runtime or with the debugger, you need to write down the inputs, outputs, and all the affects you discover. You need to take note of what’s read or written because the side effects are ultimately what matter. Knowing this information is also essential for mocking up or estimating the impacts of a change. You should sketch how each responsibility depends on its neighbors, a technique Michael Feathers calls an effect map in his book Working Effectively with Legacy Code.1

1.羽毛,迈克尔。有效地处理遗留代码。波士顿:培生教育公司,2004 年。

1.Feathers, Michael. Working Effectively with Legacy Code. Boston: Pearson Education, Inc., 2004.

重要的是要保持流程的低科技含量,这样就不会分散对手头任务的注意力。该文档工作需要专门针对特定任务,因此现在不需要使其干净和正式。但是,当您完成任务后,您可以查看笔记和草图并选择一个或者两个关键信息,它们足够通用,可以帮助完成许多任务。它们可以提升为干净的图表、附加部分或现有文档中的附加内容。您可以通过沉淀机制来增长文档(请参阅第 10 章,“避免传统文档”)。

It’s important to keep the process low tech so that it does not distract from the task at hand. This documentation work needs to be dedicated to a specific task, and there is therefore no need to make it clean and formal right now. However, when you’re done with the task, you may review the notes and sketches and select the one or two key bits of information that are general enough that they would help for many tasks. They can be promoted into a clean diagram, an additional section, or an addition within an existing document. You grow your documentation by a sedimentation mechanism (see Chapter 10, “Avoiding Traditional Documentation”).

当然,您可能有代码无法回答的问题。也许代码晦涩难懂或令人惊讶。在这种情况下,你需要帮助,最好是附近同事的帮助,而人与人之间的沟通又会重新出现。遗留系统不仅仅是代码,还有代码。维基百科上有各个时代的文档、幻灯片、旧博客文章和页面,当然现在它们在某种程度上都是错误的。

Of course, you might have questions that the code does not answer. Perhaps the code is obscure or surprising. In such a case, you need help, ideally from colleagues nearby, and human communication comes back into the picture. The legacy system is not just code; there are documents of all ages, slides, old blog posts, and pages on the wiki, and of course they are all wrong to some extent now.

遗留环境还包括一开始就在那里的人。老开发人员现在可能已经调到其他职位,但他们也许能够回答问题,特别是关于多年前做出决定的背景。

A legacy environment also includes people who were there at the beginning. The old developers may have moved to other positions now, but they might be able to answer questions, especially about the context that led to the decisions years ago.

气泡上下文

Bubble Context

即使在遗留系统中,您也希望尽可能在一切都干净整洁的理想环境中工作。如果您有一定数量的功能需要构建,那么您可能会决定在新的干净气泡上下文中构建新功能。实际上,它可以是新的特定模块、命名空间或项目,这意味着可以通过使用注释、命名约定和强制指南轻松进行记录。气泡上下文带来了在全新项目中从头开始编写软件的舒适性和效率,但又集成到更大的遗留环境中(见图14.1)。

Even in a legacy system, you want to work as much as possible in the ideal land where everything is nice and clean. If you have some number of features to build, then you might decide to build the new features in their own new clean bubble context. In practice, it can be a new specific module, namespace, or project, which means it is then easy to document by using annotations, naming conventions, and enforced guidelines. A bubble context brings the comfort and the efficiency of writing software from scratch in a brand-new project but integrated within a bigger legacy surrounding (see Figure 14.1).

下图展示了一个干净的泡沫环境集成在遗留的混乱中。 该图显示了一个单独的上下文,代表泡沫上下文,与代表法律应用的笨拙上下文相关。

图 14.1 一个干净的气泡环境集成在遗留的混乱中

Figure 14.1 A clean bubble context integrated within a legacy mess

由于气泡上下文是遗留项目中从头开始的项目,因此它也是在有限的功能区域上实践 TDD、BDD 和 DDD 以交付大量相关业务价值的完美场所。

As a bubble context is a project from scratch inside a legacy project, it is also a perfect place to practice TDD, BDD, and DDD on a limited functional area to deliver a bulk of related business value.

因此:如果您需要对遗留系统进行大量更改,请考虑创建气泡上下文。气泡上下文定义了系统其余部分的边界。在这些边界内,您可以以不同的方式重写,例如由测试驱动。在泡沫背景下,您可以通过遵循活文档方法来投资知识。相反,如果您确实需要遗留应用程序的一部分的完整文档,请考虑使用最先进的测试、代码和文档实践将该部分重写为气泡上下文。

Therefore: If you need to make a lot of changes on a legacy system, consider creating a bubble context. A bubble context defines boundaries within the rest of the system. Within these boundaries, you can rewrite in a different way, such as driven by tests. In the bubble context, you can invest in knowledge by following a living documentation approach. Conversely, if you really need full documentation of a part of a legacy application, consider rewriting that part as a bubble context, using the state-of-the-art practices for the tests, the code, and the documentation.

一开始就对气泡上下文中的代码抱有很高的期望是个好主意。其架构和指南应使用自动化工具作为一组强制指南来强制执行。例如,您可能希望禁止任何新提交对已弃用的组件进行直接引用(Javaimport或 C# )。using您可能需要并强制执行高于 90% 的测试覆盖率、无重大违规、最大代码复杂度为 2,以及方法最多有 5 个参数。

It is a good idea to start with high expectations for the code inside the bubble context. Its architecture and guidelines should be enforced using automated tools, as a set of enforced guidelines. For example, you might want to forbid any new commit from having direct references (Java import or C# using) on a deprecated component. You might require and enforce test coverage higher than 90%, no major violation, a maximum code complexity of 2, and a maximum of five parameters by method.

在编码风格上更进一步,如果您使用气泡上下文方法,您可以声明对整个气泡的严格要求,例如通过使用包级注释,如下所示:

Going further in the coding style, if you use the bubble context approach, you can declare demanding requirements for the full bubble as a whole, such as by using package-level annotations, as shown here:

1 @BubbleContext(项目名称 = "Invest3.0")
2 @不可变
3 @无空
4 @无副作用
5 包 acme.bigsystem.investmentallocation
6
7 包 acme.bigsystem.investmentallocation.domain
8 包 acme.bigsystem.investmentallocation.infra
1  @BubbleContext(ProjectName = "Invest3.0")
2  @Immutable
3  @Null-Free
4  @Side-Effect-Free
5  package acme.bigsystem.investmentallocation
6
7  package acme.bigsystem.investmentallocation.domain
8  package acme.bigsystem.investmentallocation.infra

这里的第一个注释只是声明该模块(Java 中的包或 C# 中的命名空间)是与名为 Invest3.0 的项目对应的气泡上下文的根。其他注释记录了该模块中预期的编码风格有利于不变性并避免空值和副作用。然后可以通过结对编程或代码审查来强制执行这些编码风格。

The first annotation here just declares that this module (package in Java or namespace in C#) is the root of a bubble context corresponding to a project named Invest3.0. The other annotations document that the expected coding style in this module favors immutability and avoids nulls and side effects. These coding styles can then be enforced by pair-programming or code review.

气泡上下文是由 Eric Evans 于 2013 年引入的。2气泡上下文是重写部分遗留系统的完美技术,就像在 strangler 应用程序中一样马丁·福勒设计的图案。3这个想法是重建一个一致的功能区域,逐步接管旧系统。

The bubble context was introduced by Eric Evans in 2013.2 A bubble context is a perfect technique for rewriting part of a legacy system, as in the strangler application pattern by Martin Fowler.3 The idea is to rebuild a consistent functional area that will progressively take over the old system.

2. Eric Evans,被遗留系统包围时开始使用 DDD,2013 年, http://domainlanguage.com/wp-content/uploads/2016/04/GettingStartedWithDDDWhenSurroundedByLegacySystemsV1.pdf

2.Eric Evans, Getting Started with DDD when Surrounded by Legacy Systems, 2013, http://domainlanguage.com/wp-content/uploads/2016/04/GettingStartedWithDDDWhenSurroundedByLegacySystemsV1.pdf

3. https://www.martinfowler.com/bliki/StranglerApplication.html

3.https://www.martinfowler.com/bliki/StranglerApplication.html

叠加结构

Superimposed Structure

特别是在创建集成到更大的遗留应用程序中的气泡上下文时,很难定义新旧系统之间的界限。甚至很难非常清楚地讨论它,因为很难谈论遗留系统。您希望看到一个简单明了的结构,但实际上您发现的是一个巨大的非结构化混乱(见图14.2)。

Especially when creating a bubble context integrated within a bigger legacy application, it is hard to define the boundaries between the old and new systems. It is even difficult to just discuss it very clearly because it is hard to talk about a legacy system. You would expect to see a simple and clear structure, but what you actually discover is a big unstructured mess (see Figure 14.2).

图表说明了心理预期(代表清晰的单独背景)与实际情况(代表笨拙的演示)。

图14.2 心理预期与实际情况

Figure 14.2 Mental expectations versus actual situation

即使有一个结构,它通常是任意的,并且可能会产生误导而不是有帮助(见图14.3)。

Even when there is a structure, it is often arbitrary and can mislead more than it helps (see Figure 14.3).

图中显示了一个项目结构,其中包括“com.acme”文件夹,其子文件夹分别为 com.acme.dao、com.acme.dto、com.acme.service 和 com.acme.controller。

图14.3 典型的项目结构

Figure 14.3 Typical project structure

对于遗留代码,您通常会付出大量努力来使其可测试。测试使您能够做出改变,但还不够。要进行更改,您还需要在头脑中重建遗留应用程序的心理模型。这可以是某个功能内的局部,也可以是完整的业务行为加上完整的技术架构。

With legacy code, you usually start with lots of effort to make it testable. Tests enable you to make changes but are not enough. To make changes, you also need to reconstruct a mental model of the legacy application in your head. This can be local within a function, or it can be as big as the full business behavior plus the complete technical architecture.

您需要阅读代码、采访老开发人员并修复错误才能更好地理解行为。同时,你需要用你的大脑来理解你所看到的东西。结果是您在头脑中将其投射到现有应用程序上的结构。由于现有应用程序不显示此结构,因此您可以将新的清晰结构叠加到现有应用程序上。

You need to read code, interview older developers, and fix bugs to better understand the behavior. At the same time, you need to use your brain to make sense of what you see. The result is a structure in your head that you project over the existing application. Since the existing application does not show this structure, it is up to you to superimpose a new clear structure onto the existing application.

因此:在创建气泡上下文并添加功能或修复遗留系统中的困难错误时,请创建您自己的遗留系统心智模型。在阅读遗留代码时,该模型根本不必可见。相反,旧系统的这种新结构是一个预测的愿景,一个发明。使用任何形式的文档记录这一愿景,使其成为您未来讨论和决策语言的一部分。

Therefore: In the context of creating a bubble context and adding a feature or fixing a difficult bug in a legacy system, create your own mental model of the legacy system. This model does not have to be visible at all when reading the legacy code. Instead, this new structure of the old system is a projected vision, an invention. Document this vision using any form of documentation, so that it becomes part of your language for future discussions and decisions.

这个新结构是一种幻觉,一种愿景,它并不是直接从当前构建的系统中提取的。您可能会将其视为对系统的描述,因为它应该被构建,而不是它是如何构建的,回想起来,现在每个人都知道得更好。

This new structure is a hallucination, a vision, that is not directly extracted from the system as it is currently built. You might see it as a description of the system as it should have been built as opposed to how it is built, in retrospect, now that everyone knows better.

您可以将新模型显示为遗留系统顶部的叠加结构,或者显示给所有相关人员的简单草图。我们希望展示新结构与当前状态的关系,但是一旦您想要一些细节,就很难实现,因为当前系统可能具有完全不同的结构。您可以花时间将其制作成合适的幻灯片,以便在路演期间向每个利益相关者展示。相反,您可能决定使其在代码本身中可见,以使其更加明显,并为进一步转换铺平道路。

You can show the new model as a superimposed structure on top of the legacy system, as a plain sketch that you show to everyone involved. It is desirable to show how the new structure relates to the current state, but it can be too hard to achieve as soon as you want some details, given that the current system may have a totally different structure. You can invest time in making it a proper slide deck to present to every stakeholder during a roadshow. You might instead decide to make it visible within the code itself to make it more obvious and to pave the way for further transformations.

以下是叠加在遗留系统之上的心智模型的一些示例:

The following are some examples of mental models superimposed on top of legacy systems:

  • 业务管道:这种业务视角类似于销售人员的标准销售漏斗。它将系统作为一个阶段管道,按照典型用户旅程中发生的顺序进行:访问者导航目录(目录阶段),将商品添加到购物车(购物车阶段),查看订单(订单阶段)准备阶段),付款(付款阶段),收到确认和产品,如果出现问题,获得售后服务。该模型假设每个阶段的体积都会减少很大一部分,这对于从技术和操作角度设计每个阶段来说都是一个很好的见解。

  • Business pipeline: This perspective of the business is similar to the standard sales funnel of salespeople. It focuses on the system as a pipeline of stages in the order in which they happen in a typical user journey: A visitor navigates the catalog (catalog stage), adds items to the shopping cart (shopping cart stage), reviews the order (order preparation stage), pays (payment stage), receives a confirmation and the product, and gets after-sale service if things go wrong. This model assumes that the volume decreases by a large factor at each stage, which is a nice insight for designing each stage technically and from an operational point of view.

  • 主要业务资产,如资产捕获(Martin Fowler):此视角侧重于业务领域的两个或三个主要资产,例如电子商务系统中的客户和产品。每个资产都可以被视为一个维度,它本身可以分为多个部分,例如客户部分和产品部分。

  • Main business assets, as in asset capture (Martin Fowler): This perspective focuses on the two or three main assets of the business domain, such as the customer and the product in the case of an e-commerce system. Each asset can be seen as a dimension that can itself be split into segments, such as customer segments and product segments.

  • 域和子域,或有界上下文 (Eric Evans):这种观点需要在 DDD 和整体业务领域方面具有一定的成熟度,但它也具有最大的好处。

  • Domains and subdomains, or bounded contexts (Eric Evans): This perspective requires some maturity in terms of both DDD and the overall business domain, but it also has the most benefits.

  • 责任级别:从业务角度来看,有运营、战术和战略级别。Eric Evans 在他的《领域驱动设计》一书中提到了这一点。

  • Levels of responsibility: There are operational, tactical, and strategic levels, from a business perspective. Eric Evans mentions this in his book Domain-Driven Design.

  • 这些视图的混合:例如,您可能会考虑三个维度:客户、产品和处理,每个维度又分为阶段、客户细分和产品细分。您还可以混合从左到右的业务管道以及自下而上的运营、战术和战略级别。

  • A mix of these views: For example, you might consider three dimensions— customer, product, and processing—each segmented into stages, customer segments, and product segments. You can also mix a business pipeline from left to right and the operational, tactical, and strategic levels from the bottom up.

不管是哪种叠加结构,一旦有了它,谈论系统就变得更简单了。例如,您可能会建议“重写有关支付阶段的所有内容,从可以作为第一阶段下载的产品开始”。或者您可能决定“仅为 B2B 客户重写目录部分”。当你有一个叠加的结构时,沟通会变得更加有效。

Whatever the superimposed structure, once you have it, it becomes simpler to talk about the system. You might, for example, propose to “rewrite everything about the payment stage, starting with products that can be downloaded as a first phase.” Or you might decide to “rewrite the catalog part only for B2B customers.” Communication becomes more efficient when you have a superimposed structure.

然而,团队的每个成员都应该按照他们的方式来解释这些句子。因此,使叠加结构更加明显是有用的。

However, it is up to every member of the team to interpret these sentences the way they see them. It is therefore useful to make the superimposed structure more visible.

突出显示的结构

Highlighted Structure

叠加结构可以链接到现有代码。如果幸运的话,叠加的结构和代码现有结构之间的映射只是大量杂乱的一对一关系。如果你不幸运,这可能是一项不可能完成的任务。

The superimposed structure can be linked to the existing code. If you’re lucky, the mapping between the superimposed structure and the existing structure of the code is just a large number of messy one-to-one relationships. If you’re not lucky, this can just be an impossible task.

您可以在每个元素上添加叠加结构的内在信息。例如,图 14.4中所示的一个 DTO是计费域的一部分,另一个 DTO 是目录域的一部分,依此类推。

You can add the intrinsic information of the superimposed structure on each element. For example, one DTO shown in Figure 14.4 is part of the billing domain, another is part of the catalog domain, and so on.

图中显示了技术结构和业务驱动结构之间映射的示例。

图14.4 技术结构和业务驱动结构之间的映射示例

Figure 14.4 Example of mapping between a technical structure and a business-driven structure

为了使新结构可见,您可以在类、接口、方法甚至模块或项目级文件上使用注释。一些 IDE 还提供了对文件进行标记以便对文件进行分组的方法,但这取决于 IDE,并且标记通常不是存储在文件本身中。在以下示例中,您将使用注释标记每个模块中的类,以表示它们所涉及的子域:

To make the new structure visible, you can use annotation on classes, interfaces, methods, and even modules or project-level files. Some IDEs also offer ways to tag files in order to group them, but it depends on the IDE, and the tags are not usually stored within the files themselves. In the following example, you would tag the classes in each module with annotations to denote the subdomain they are about:

1个模块DTO
2 OrderDTO @ShoppingCart
3 BAddressDTO @Billing
4 ProductDTO @Catalog
5 ShippingCostDTO @Billing
1  module DTO
2    OrderDTO @ShoppingCart
3    BAddressDTO @Billing
4    ProductDTO @Catalog
5    ShippingCostDTO @Billing

这将有助于为下一步做好准备:将处理计费的类移动到同一个计费模块中。但即使在执行此操作之前,如果您通过注释进行搜索,您的代码现在也具有显示业务领域的显式结构@Billing

This will help prepare for the next step: Move the classes that deal with billing into the same billing module. But even before you do that, your code now has an explicit structure showing the business domain if you search by the @Billing annotation:

1个计费模块
2 BillingAddressDTO //重命名以修复缩写
3 运费DTO
4 运费配置
5 运费@服务
1  module Billing
2    BillingAddressDTO //renamed to fix the abbreviation
3    ShippingCostDTO
4    ShippingCostConfiguration
5    ShippingCost @Service

叠加结构的最终目的应该是成为系统的主要结构,使其不再叠加。不幸的是,在许多情况下,这种情况永远不会发生,因为努力不会达到“最终状态”。然而,这种方法仍然很有价值,因为它可以帮助同时交付宝贵的业务价值。即使遗留代码的结构很糟糕,只要您使用更好的结构对其进行推理,您就可以获得更好的决策的好处。

The end purpose of a superimposed structure should be to become the primary structure of the system so that it is no longer superimposed. Unfortunately, in many cases, this never happens because the effort will not reach the “end state.” However, this approach is still valuable because it can help deliver precious business value in the meantime. Even if the legacy code is badly structured, as long as you reason about it using a better structure, you are getting the benefits of better decisions.

外部注释

External Annotations

有时我们不想仅仅为了向其添加一些知识而接触一个脆弱的系统。有时,仅仅为了添加额外的注释就很难在大型代码库中进行接触和提交。您不想冒险引入随机回归。您不想触及提交历史记录。它可能很难构建,除非绝对必要,否则您不想构建它。或者你的老板可能根本不允许你“仅仅为了文档”而更改代码。

Sometimes we don’t want to touch a fragile system just to add some knowledge to it. It is sometimes hard to touch and commit in large code bases just to add extra annotations. You don’t want to risk introducing random regressions. You don’t want to touch commit history. It may be so hard to build that you don’t want to build it unless absolutely necessary. Or your boss may not allow you to change the code at all “just for documentation.”

在这种情况下,仍然可以应用大多数活文档技术,除了文档的内部手段(例如注释、命名约定)必须用外部文档替换之外。例如,您可能需要一个将包名称映射到标签的文本文件:

In such a situation, it is still possible to apply most techniques of living documentation, except that the internal means of documentation (for example, annotations, naming conventions) have to be replaced with an external document. For example, you might need a text file that maps package names to tags:

1 acme.phenix.core = DomainModel 车队管理
2 acme.phenix.allocation = DomainModel 调度
3 acme.phenix.spring = 基础设施调度
4 ...
1  acme.phenix.core = DomainModel FleetManagement
2  acme.phenix.allocation = DomainModel Dispatching
3  acme.phenix.spring = Infrastructure Dispatching
4  ...

有了这样的文档,就可以构建解析源代码并利用这些外部注释的工具,就像它们利用常规内部注释一样(见图14.5)。

With such a document, it is possible to build tools that parse the source code and exploit these external annotations just as they would exploit the regular internal ones (see Figure 14.5).

一张图展示了registry的使用。 旧文件夹中的点表示连接到注释注册表中存在的 dto、dao 和服务。

图14.5 使用注册表使代码不被触及

Figure 14.5 Using a registry so that the code is not touched

这种方法的问题在于它是一种外部文档,因此很容易受到遗留系统中的更改的影响。例如,如果您在旧系统中重命名了包,则必须更新相关的外部注释。

The issue with this approach is that it is an external kind of documentation, and hence it is fragile to changes in the legacy system. If you ever rename a package in the legacy system, for example, you have to update the related external annotations.

可生物降解转化

Biodegradable Transformation

临时流程的文档在完成后应该随之消失。许多遗产野心涉及从一种状态到另一种状态的转变。这样的转变可能需要数年时间,而且可能永远不会真正达到最终状态。但是,您需要向所有团队解释这种转变,并且应该将其显示为实时文档的一部分。

Documentation of a temporary process should disappear with it when it is done. Many legacy ambitions involve transformations from one state to another. Such a transformation might take years and may never really reach the end state. However, you need to explain this transformation to all teams, and you should show it as part of your living documentation.

示例:Strangler 应用程序

Example: Strangler Application

假设您正在构建一个扼杀者应用程序,预计随着时间的推移,该应用程序将取代旧的应用程序。这个扼杀者应用程序可能会生活在它自己的泡沫环境中。您可以将此气泡上下文注释为扼杀应用程序。然而,它扮演扼杀者的角色是一个暂时的事实,并不一定是新应用程序固有的;当它成功地扼杀了旧的应用程序时,它就只是名义上的应用程序,它的注释将毫无意义。因此,绞杀剂的应用策略是可生物降解的转化

Say that you’re building a strangler application that is expected to replace an older application over time. This strangler application will probably live in its own bubble context. You could just annotate this bubble context as being a strangler application. However, that it is playing the role of a strangler is a temporary fact and is not necessarily intrinsic to the new application; when it has successfully strangled the old one, it will just become the nominal application, and its annotation will be meaningless. The strangler application strategy is therefore a biodegradable transformation.

与此同时,每个开发人员都需要知道使用新的扼杀者应用程序而不是被扼杀的应用程序。因此,您需要StrangledBy("new bubble context application")向被扼杀的应用程序添加注释,以解释扼杀正在等待处理(参见图 14.6)。当可以安全删除它时,注释将随之消失。

In the meantime, every developer needs to know to use the new strangler application instead of the one being strangled. Therefore you’d need to add a StrangledBy("new bubble context application") annotation to the strangled application to explain that a strangling is pending (see Figure 14.6). When it can be safely deleted, the annotation will go away with it.

图片

图14.6 一个应用程序被注释为被另一个应用程序扼杀

Figure 14.6 An application annotated as being strangled by another

当然,您仍然可以标记新应用程序StranglerApplication,但完成后您最终必须清理该标记。如果扼杀从未完成,这将通过暗示指向未完成的行动。

Of course, you could still tag the new application StranglerApplication, but you will have to clean up this tag eventually, when you are done. And if the strangling never completes, this will by a hint pointing to the unfinished initiative.

示例:破产

Example: Bankruptcy

一些遗留应用程序非常脆弱,每当您尝试更改它们时它们都会崩溃,并且需要数周的工作才能再次稳定它们。当您认识到这一点时,您可能会决定正式宣布此类应用程序“破产”,这意味着任何人都不应更改它们。

Some legacy applications are so fragile that they break any time you try to change them, and it takes weeks of work to stabilize them again. When you recognize this, you might decide to declare such applications officially “bankrupt,” which means nobody should ever change them.

在新应用程序扼杀旧应用程序的大型遗留系统中,您不希望同时对两个应用程序执行维护,因此您也可以将旧应用程序标记为“冻结”或“破产”。您可以通过多种方式将应用程序标记为破产:

In large legacy systems with new applications strangling older ones, you don’t want to perform maintenance on two applications at the same time, so you can mark the older one as “frozen” or “bankrupt,” too. You can mark the application as bankrupt by using a number of means:

  • AssemblyConfig在包上使用注释或在文件中使用属性

  • Use annotation on the package or an attribute in the AssemblyConfig file

  • 使用 BANKRUPTCY.txt 文件来解释您需要了解和要做的事情(或避免做的事情)

  • Use a BANKRUPTCY.txt file to explain what you need to know and to do (or avoid doing)

  • 取消所有人的commit权限,如果有人试图commit并问为什么不可能,就趁机解释说已经破产了

  • Remove the commit privilege from everyone, and if anyone tries to commit and asks why it is not possible, take the opportunity to explain that it is bankrupt

  • 作为较弱的替代方案,监视提交并在破产应用程序中满足更改时发出警报

  • As a weaker alternative, monitor the commits and raise an alert when changes are met in a bankrupt application

同意格言

Agree on Maxims

对遗留系统的重大改变是由许多有着共同目标的人进行的;您可以使用格言来分享愿景,正如《面向对象的重新设计模式》一书中所建议的那样。4

Big changes to legacy systems are made by a number of people who share common objectives; you can use maxims to share the vision, as advised in the book Object-Oriented Reengineering Patterns.4

4. Demeyer、Serge、Stéphane Ducasse 和 Oscar Nierstrasz,面向对象的重新设计模式。旧金山:摩根考夫曼出版社,2003 年。

4.Demeyer, Serge, Stéphane Ducasse, and Oscar Nierstrasz, Object-Oriented Reengineering Patterns. San Francisco: Morgan Kaufmann Publishers, 2003.

当您制定遗留转型策略时,您需要确保每个人都了解它。您可能已经创建了一个叠加结构。您可能已经在项目代码中注释了您的气泡上下文。然而,在您需要与每个人分享的所有事情中,有一些关键决定您确实希望每个人始终牢记在心。格言是解决此类情况的有力答案,并且它们已经存在了很长时间。

When you have a legacy transformation strategy, you need to make sure everyone knows about it. You may have created a superimposed structure. You may have annotated your bubble context in the code of the project. However, of all the things you need to share with everyone, there are a few key decisions you really want everyone to keep in mind at all times. Maxims are a powerful answer for such situations, and they have been around for ages.

因此:发明格言,将最关键的知识传播给每个人。经常重复这些格言来宣传它们。让它们押韵以增强效果。

Therefore: Invent maxims to spread the most critical pieces of knowledge to everyone. Repeat these maxims often to advertise them. Make them rhyme to boost their effect.

当您的项目仅重写大型遗留系统的一部分,并且您不想重写现在绝对有用的内容(即计费引擎)而没有其他内容时,您可以使用这样的格言:“一次一个工作地点(计费引擎)。” 这是我在大型遗留项目中最喜欢的格言之一。目的是提醒大家在做项目的时候不要分心;他们只需要关注主要工地。

When your project is to rewrite only a portion of a large legacy system, and you don’t want to rewrite more than what’s absolutely useful now (that is, the billing engine) and nothing else, you can use a maxim like this: “One work site at a time (the billing engine).” This has been one of my favorite maxims in a big legacy project. It was meant to remind everyone not to get distracted when working on the project; they needed to focus on the main worksite only.

“在罗马做到入乡随俗。” 这与单一工作地点的格言相对应。换句话说,当你碰巧走到主要工作场所之外时,不要有太大的创新或改变;即使您不喜欢,也只需按照当地风格做最少的事情。在处理不会被重写的遗留代码时要保守。

“When in Rome, do as the Romans do.” This was the counterpart to the single-work-site maxim. In other words, when you happen to walk outside the main work site, don’t innovate or change much; just do the minimum in the local style, even if you don’t like it. Be conservative when working in the legacy code that will not be rewritten.

吉尔斯·菲利帕特 (Gilles Philippart) 提出的另一条非常有力的格言是:“不要喂怪物!(不要改进遗留的大泥球;这只会让它活得更久)。”

Another legacy maxim that was proposed by Gilles Philippart is an extremely powerful one: “Don’t feed the monster! (Don’t improve the legacy Big Ball of Mud; it would only make it live longer).”

我发现格言是一种有价值的文档形式。关键是只要有意义就经常重复它们,最好每天至少一次。maxim 格式是为了坚持下去,这就是为什么你可能想尝试一下。Maxims 还可以帮助分享团队商定的团队回顾的结论。

I’ve found maxims to be a valuable form of documentation. The point is to repeat them often, whenever it makes sense to do so, ideally at least once a day. The maxim format is made to stick, and that is why you might want to give it a try. Maxims can also help share the conclusions of your team retrospectives, as agreed upon by the team.

强制执行的旧规则

Enforced Legacy Rules

遗留改造的持续时间比进行改造的人持续的时间更长;自动执行重大决策以保护他们。

Legacy transformations can last longer than the people doing them; automate the enforcement of the big decisions to protect them.

假设在遗留应用程序中,您已决定不应再调用某些方法,除非从一个特定位置调用。例如,您可能决定将读写遗留应用程序转换为遗留读取模型,除了负责将此读取模型与其他权威模型同步的侦听器之外,该模型不应接受任何更新请求。设计决策可以在决策日志中说明:

Say that in a legacy application, you’ve decided that some method should not be called anymore, except from one specific place. For example, you might have decided to turn a read-write legacy application into a legacy read model, which should not accept any request to update it except from the listener responsible for syncing this read model from the other authoritative model. The design decision can be stated in a decision log:

该模型是一个读取模型。因此它是只读的。不要调用此 Save 方法,除非您是从权威写入模型发送的事件同步此读取模型的侦听器。

This model is a read model. It is therefore read-only. Don’t call this Save method, unless you are the listener that syncs this read model from the events sent from the authoritative write model.

您可以在其中包含以下理由:

You might include with it the following rationale:

遗留系统已被证明难以维护,因此我们不想再在其中进行开发。这就是为什么我们要构建另一个系统作为替代品。但由于它集成了很多外部系统,我们可以简单地一次性将其删除。这就是为什么我们决定保留这个旧系统作为遗留读取模型以用于集成目的。

The legacy system has proven to be unmaintainable, so we don’t want to develop in it anymore. This is why we’re building another system as a replacement. But because so many external systems are integrated with it, we can simply remove it in one go. This is why we have decided to keep this old system just as a legacy read model for integration purposes.

您也可以直接在代码中记录这一点:

You could also document this directly in the code:

  • 使用自定义注释标记设计决策@LegacyReadModel,并包含消息和基本原理

  • Mark the design decision with the custom annotation @LegacyReadModel and include the message and the rationale

  • 将方法标记为@Deprecated

  • Mark the method as @Deprecated

然而,处于遗留系统中也意味着周围可能存在遗留团队,其中一些是远程团队或在其他部门,并且您永远无法确定他们会阅读您的文档或电子邮件,或者当您提到设计决策时他们会注意在每日站立会议中。而且您知道,如果某些开发人员不尊重设计决策,就会发生不好的事情。由于不一致的数据管理策略,您会遇到错误并付出额外的意外复杂性的代价。

However, being in a legacy system also means there are probably legacy teams around, some of them remote or in other departments, and you can never be sure they will read your documentation or emails or that they will pay attention when you mention the design decision in the daily standup. And you know that if some developers don’t respect the design decision, bad things will happen. You’ll get bugs and pay the cost of extra accidental complexity due to the inconsistent data management strategies.

我的同事伊戈尔·洛维奇 (Igor Lovich) 提出了一种简单的方法来记录此类决定作为强制执行指南。假设您按如下方式表达设计决策:

My colleague Igor Lovich came up with a simple way to document such a decision as an enforced guideline. Say that you express a design decision as follows:

除非您位于负责同步的一两个类的白名单中,否则切勿调用此已弃用的方法。

Never call this deprecated method unless you’re in the white-list of the one or two classes responsible for the sync.

这是一个自定义设计规则,可以在运行时使用一些附加代码强制执行:

This is a custom design rule that can then be enforced at runtime with some additional code:

  • 捕获方法中的堆栈跟踪,以找出谁在调用它,并确保它是允许的代码段(例如,在 a 中引发异常try-catch并在 Java 中提取其堆栈跟踪)。

  • Capture the stack trace in the method to find out who’s calling it and ensure that it’s the allowed piece of code (for example, throw an exception within a try-catch and extract its stack trace in Java).

  • 检查堆栈跟踪中至少有一个调用者属于允许调用者方法的白名单。

  • Check that at least one caller in the stack trace belongs to the white-list of allowed callers’ methods.

  • assert如果您想在某些环境中而不是在所有环境中快速失败,请将检查放入 Java 中。

  • Make the check into a Java assert, if you want to fail-fast in some environments but not all of them.

  • 当检查失败时以将触发特定后续行动的方式记录。(如果它被解雇,那么它实际上是一个缺陷。)

  • Log when the check fails in a way that will trigger specific follow-up. (If it gets fired, then it’s actually a defect.)

此外,你可以改变格言“不要喂怪物!” (不要改进遗留)”也成为强制遗留规则,通过禁止提交到代码库的特定区域。或者,当提交完成时,您可能会发出警告。这种执行比人们容易错过或忽视的冗长解释更简单、更有效。

In addition, you could turn the maxim “Don’t feed the monster! (Don’t improve the legacy)” into an enforced legacy rule, too, by forbidding commits into a particular area of the code base. Or you might raise a warning when a commit is done there. Such enforcement is simple and more effective than long explanations that people tend to miss or ignore.

在实践中,遗留系统使一切比预期更加复杂。需要勇气和一些创造力才能想出相对“不太糟糕”的解决方案!

In practice, legacy systems make everything more complicated than expected. It takes courage and some creativity to come up with relatively “not too bad” solutions!

概括

Summary

遗留系统对活文档提出了广泛的挑战。他们对代码及其知识抱有悲观的看法,这些知识大部分都存在,但被混淆为化石知识,因此您需要特殊的技术来使其再次可访问,例如叠加结构和突出显示的结构。当代码太脆弱或无法合理更改时,您必须求助于外部注释。

Legacy systems raise extensive challenges with respect to living documentation. They come with a pessimistic perspective of the code and its knowledge, which is mostly there but is obfuscated as a fossilized knowledge, so you need ad hoc techniques to make it accessible again, like superimposed structure and highlighted structure. When the code is too fragile or can’t reasonably be changed, you must resort to external annotations.

因为对遗留的关注通常是在遗留迁移的背景下进行的,所以它意味着重大更改,添加和删除整个部分 - 所有这些更改都是由许多人在很长的时间跨度内执行的:这需要可生物降解的文档,这意味着删除的内容会消失代码。然后,除了工件中具体化的知识之外,您还需要一种让人们以某种​​一致性行事的方式,例如通过共享的格言。显然,你还需要很大的勇气!

Because a focus on legacy is often in the context of a legacy migration, it implies major changes, adding and removing entire sections—all these changes performed by many people over large time spans: this calls for biodegradable documentation means that go away with the deleted code. Then beyond the knowledge materialized in the artifact, you also need a way for people to act with some consistency, for example through shared maxims. And obviously, you also need a lot of courage!

第15章

Chapter 15

额外:显眼的文档

Extra: Conspicuous Documentation

关于沟通最常见的误区是它确实发生了。

The most common myth of communication is that it happened.

—@ixhd 来自 Twitter

—@ixhd from Twitter

仅仅因为您拥有适当的文档机制并不能保证人们会注意到它、记住知识或为其做出贡献。有很多技巧可以用更少的文字、更快、更准确、不浪费人们的时间,并且以一种有趣的方式传达信息。使用此类技术可以帮助将活文档融入您的文化,从而提高活文档的效率。这一额外的章节列举了其中一些技术。

Just because you have a documentation mechanism in place does not guarantee that people will notice it, remember the knowledge, or contribute to it. There are many techniques to get the message through with fewer words, more quickly, more accurately, without wasting people’s time, and in a fun way. Using such techniques can boost the efficiency of your living documentation by helping to infuse the living documentation into your culture. This extra chapter enumerates some of these techniques.

关注差异

Focusing on Differences

当描述一个特定的事物(例如狗)时,我们关注它与一般事物(例如哺乳动物)的差异。如果在提到具体事物之前,一般事物是众所周知的或得到了很好的描述,那么我们可以只用几个点来描述具体事物——每个显着差异一个。

When describing a specific thing, such as a dog, we focus on its differences from the generic thing, such as a mammal. If the generic thing is well known or well described before the specific thing is mentioned, we can describe the specific thing with just a few points—one for each significant difference.

这里的重要概念是显着性,定义为“引人注目的点或特征”。1我们主要想描述大量信息中的要点。

The important concept here is salience, defined as “a striking point or feature.”1 We primarily want to describe the salient points out of the mass of information.

1.经许可。来自 Merriam-Webster.com © 2019 Merriam-Webster, Inc. https://www.merriam-webster.com/dictionary/salience

1.By Permission. From Merriam-Webster.com © 2019 by Merriam-Webster, Inc. https://www.merriam-webster.com/dictionary/salience.

你的柠檬怎么样?

How Is Your Lemon?

在 Øredev 2013 期间我参加的一次培训课程中,Joe Rainsberger 在讨论 BDD 时讲述了一个关于柠檬的故事。我不记得确切的故事,但我记得关键的见解,所以这是我自己对这个故事的完全扭曲的描述:

During a training session I’ve attended during Øredev 2013, Joe Rainsberger told a story about lemons while discussing BDD. I don’t remember the exact story, but I do remember the key insight, so here’s my own totally distorted account of the story:

培训师让大家描述一下柠檬是什么样的。该小组描述了柠檬的典型形状、黄色、酸味和颗粒状质地。然后,培训师给每位参加者一个真正的柠檬,并要求他们仔细研究柠檬几分钟。

The trainer asked everyone to describe what a lemon’s like. The group described the typical lemon shape, yellow color, acid taste, and grained texture of a lemon. The trainer then gave each attendee a real lemon and asked them to carefully study their lemons for a few minutes.

培训师还分析了他自己的柠檬。柠檬的一端以一种奇怪的方式弯曲。中间某处有颜色变化。与普通柠檬相比,柠檬有点小。

The trainer also analyzed his own lemon. One end of the lemon was bent in a weird way. There was a variation of color somewhere in the middle. The lemon was kind of small compared to the average lemon.

然后,他要求每个人将自己的柠檬放回篮子中,然后要求每个人在所有其他柠檬中认出自己的柠檬。这太简单了!每个与会者都意识到他或她已经非常深入地了解了特定的柠檬。“这是我的柠檬!” 他们都说!他们甚至对自己的柠檬有一点依恋。

He then asked everyone to put their lemons back together into the basket, and then asked each person to recognize his or her lemon among all the other lemons. This was surprisingly easy! Each attendee realized that he or she had learned a particular lemon very intimately. “It’s my lemon!” they all said! They even felt a bit of attachment to their individual lemons.

通过仔细观察特定的柠檬与每个人都知道的柠檬的通用概念的对比,您可以非常有效地描述它。您的描述既准确又详细,而且高效,因为您不必描述通用的事物。

By looking carefully at a specific lemon in contrast to the generic concept of a lemon that everybody knows, you can describe it very effectively. Your description is at the same time precise with lots of details and efficient because you don’t have to describe the generic thing.

我见过同事使用这种技术来描述业务领域的概念。例如,在向新加入者介绍金融资产类别时,培训师仅提到特定资产类别(例如商品)特有的五到七个要点,这与一般众所周知的资产类别(例如商品)形成鲜明对比。股票。

I’ve seen colleagues use this technique to describe concepts from a business domain. For example, during a presentation to new joiners on financial asset classes, the trainer was mentioning only the five to seven bullet points that were distinctive to a particular asset class, such as commodities, in contrast to a generic well-known asset class such as equities.

在电力市场中,一个特殊性是,与公司股票相比,价格在白天和一年中都具有很强的季节性。在石油市场,地理位置很重要,因为石油不是随处运输的。

In the electricity market, a specificity is that the prices are very seasonal during the day and during the year, in contrast to company stocks. In the oil market, geography matters, as oil is not shipped just anywhere.

只告诉未知的事情

Tell Only What’s Unknown

向已经了解某件事的人解释它是没有意义的。关键是要确定观众知道什么。在对话过程中,可以根据与你交谈的人的问题、观察他们的肢体语言并直接询问他们,断言他们已经知道或不知道什么。以书面形式来说,这比较困难,但并非不可能。有几种方法可以做到这一点。

There is no point in explaining something to people who already know it. The key is to identify what the audience knows. During conversations, it is possible to assert what the people you’re talking to already know or don’t know, based on their questions, by watching their body language, and by asking them directly. In written form, this is more difficult, but it is not impossible. There are several ways to do it.

按已知受众细分

Segmenting by Known Audience

对于每个受众,您都可以获得有关最常见问题的反馈,如果有支持团队,您可以与他们交谈以更好地了解众所周知的内容以及需要进一步解释的内容。然后您就可以专注于每个受众未知的内容。

For each audience, you can get feedback on the most frequently asked questions, and if there’s a support team, you can talk to them to better learn what’s well known and what needs to be explained more. Then you can focus on what’s unknown for each audience.

灵活的内容

Flexible Content

您应该组织书面内容,以便可以略读、跳过和部分阅读。您还应该清楚地标记可选部分,并使标题提供足够的信息,以便读者可以决定某个部分是否是他们想要的。

You should organize written content so that it can be skimmed, skipped, and read partially. You should also clearly mark optional sections and make titles informative enough so that readers can decide whether a section is what they are after.

例如,马丁·福勒建议写双面书。2这个想法是将内容分为两大部分,第一部分是旨在从头到尾阅读的叙述性内容,第二部分是不适合从头到尾阅读的参考材料。您阅读第一部分是为了全面了解该主题,其余部分则保留在您真正需要时使用。

For example, Martin Fowler suggests writing duplex books.2 The idea is to split the content into two big parts, where the first part is a narrative designed to be read cover to cover, and the second part is reference material that is not meant to be read cover to cover. You read the first part to get an overall understanding of the topic, and you keep the rest for when you actually to need it.

2. Martin Fowler,ThoughtWorks 博客, http://www.martinfowler.com/bliki/DuplexBook.html

2.Martin Fowler, ThoughtWorks blog, http://www.martinfowler.com/bliki/DuplexBook.html

低保真内容

Low-Fidelity Content

只要您希望人们感到被邀请添加他们的输入,就可以使用低保真度表示形式进行输出。

Use low fidelity representation for output as long as you want people feel invited to add their input.

—推特上的@kearnsey

—@kearnsey on Twitter

用于集思广益、探索或提出想法的图表常常被误解为一份规范。这会导致对细节的过早反馈,例如“我更喜欢另一种颜色”,即使整个事情在接下来的几个小时或几天内会发生很大的变化。这种情况对于在计算机上完成的所有工作尤其如此,因为使用适当的软件可以快速轻松地创建美观的文档、图片和图表。

Too often a diagram that was meant for brainstorming, exploring, or proposing ideas is misunderstood as a piece of specification. This results in premature feedback on details such as “I’d prefer another color,” even though the whole thing will change a lot in the next hours or days. This situation is especially true for everything done on a computer, since it is quick and easy to create nice-looking documents, pictures, and diagrams using the proper piece of software.

因此:当知识还在成型时,通过使用线框图和粗略渲染等低保真内容在文档中明确说明。

Therefore: When the knowledge is still being shaped, make it clear in the documents by using low-fidelity content such as wireframes and sketchy rendering.

视觉引导

Visual Facilitation

当用手指指向白板或屏幕上的图表上的方框时(参见图 15.1 ),“我正在谈论这个”比“我正在谈论这个负责过滤的东西”要简洁和精确得多。上游的重复条目实时辅助计算引擎。” 正如瑞纳特·阿卜杜林 (Rinat Abdulin) 在 Twitter 上我们进行的一次关于动态图表的对话中所说的那样,“在讨论过程中‘你可以指出’的东西有助于更快、更准确地进行沟通。” 由视觉媒体支持的对话是一项强大的技术。

“I’m talking about that” when pointing a finger at a box on a diagram on the whiteboard or on a screen (see Figure 15.1) is much more concise and precise than “I’m talking about this thing that takes care of filtering the duplicate entries upstream of the real-time secondary calculation engine.” As Rinat Abdulin said on Twitter during a conversation we had about living diagrams, “Stuff ‘you can point on to’ during discussions helps communicate faster and with better accuracy.” Having conversations supported by visual media is a powerful technique.

下图说明,当您可以将手指指向共享视觉支持上正在谈论的内容时,沟通会得到改善。 该图显示了一个人的手指指向一张纸上绘制的图表中的特定块。

图 15.1 当您可以将手指指向共享视觉支持上正在谈论的内容时,沟通会得到改善

Figure 15.1 Communication is improved when you can point your finger at what you’re talking about on a shared visual support

在会议或开放空间会议期间,活动挂图上的视觉注释不仅报告所讲内容:它们还通过出现在每个人的眼前来影响进一步的讨论。如果在白板上使用记号笔的抄写员善于视觉引导,这种影响会更加强烈。他或她重新安排信息的组织方式,对概念进行排序,使用有意义的布局,注明链接,进行旁注,并对讨论中涉及的内涵进行一些修饰。

During meetings or an open-space session, the visual notes on the flipchart not only report on what has been said: they also influence further discussions just by being in front of everyone’s eyes. This influence is even stronger if the scribe who has the marker on the whiteboard is skilled in visual facilitation. He or she rearranges the way the information is organized, sorting concepts, using a meaningful layout, noting links, making side remarks, and drawing little decorations about the connotations involved in the discussions.

因此:在讨论过程中不要忽视视觉支持的重要性。投资一些视觉促进技能,并了解视觉支持如何帮助塑造工作的动力。

Therefore: Don’t discount the importance of visual supports during discussions. Invest in some visual facilitation skills and learn how visual supports can help shape the dynamics of the work.

视觉笔记对于已经说过的内容来说是多余的,因此如果您没有立即理解某个单词或想法,那么视觉笔记会有所帮助。他们可以帮助大家跟进并帮助每个人继续参与会议。如果做得好,视觉引导也是让人们微笑的一个机会,这要归功于一些视觉幽默。

Visual notes are redundant with has been said and therefore help if you did not catch a word or an idea immediately. They help as a way to catch up and help everyone remain involved in the session. When done well, visual facilitation is also an opportunity to make people smile, thanks to some visual humor.

便于搜索的文档

Search-Friendly Documentation

仅仅提供信息是不够的。您必须知道在需要时在哪里可以找到您需要的东西,并且它需要易于搜索。

Making information available is not enough. You have to know where to find what you need when you need it, and it needs to be easily searchable.

易于搜索首先是使用独特的词语。

Being easily searchable is first of all a matter of using distinctive words.

特色词

Distinctive Words

“Go”作为一种编程语言的名称,来自像谷歌这样涉足搜索的公司,对搜索不友好。一个更易于搜索的名称是 golang。

“Go” as a name of a programming language, from a company like Google that is into search, is not search friendly. A more search-friendly name is golang.

该知识应该清楚地提及用户需要它解决的问题,因为这是将要搜索的问题。为了帮助解决此问题,应添加关键字,包括实际内容中并未真正出现但有人在搜索时可能会使用的单词。例如,使用来自实际用户的词语(例如从失败搜索的分析中找到的词语)会很有帮助。

The piece of knowledge should mention clearly the user needs it addresses, since this is the question that will be searched for. To help with this, keywords should be added, including words that don’t really occur in the actual content but that are likely to be used when someone is searching for it. It is helpful to use words from actual users, found from the analytics of failed searches, for example.

请记住提及同义词、反义词、误译和常见混淆,以提高搜索的可发现性。

Remember to mention synonyms, antonyms, mistranslations, and common confusions for improved discoverability by search.

所有这些通常只考虑传统文档中的书面文本,但它也适用于代码,代码也被视为文本。您甚至可以使用注释来添加关键字。

All this is usually considered only for written text in a traditional document, but it applies just as well in the code, which is considered text, too. And you may even use annotations to add keywords.

具体例子,现在一起

Concrete Examples, Together, Now

在讨论规范时,请确保每位与会者就具体示例达成一致。

Make sure to have every attendee agree on concrete examples when discussing specifications.

这听起来可能很熟悉:

This probably sounds familiar:

既然我们就这一改变达成了一致,我们就可以停止这次会议了。您将负责测试用例以及详细设计和屏幕模型,我们将在下周讨论它们。在此期间,如果您有任何疑问,请随时询问。

Now that we’re in agreement on this change, we can stop this meeting. You will work on test cases and the detailed design and screen mockup, and we’ll discuss them next week. In the meantime, feel free to ask if you have any questions.

这里失去的机会是,每个参与的人很可能会在会议结束后浪费时间。会议期间达成的集体协议往往是一种幻想。俗话说:“细节决定成败”。只有当开始为新屏幕创建模型时,问题才会真正开始显现出来。只有在尝试对抽象需求进行编码时,才会发生误解,并且可能要在几天或几周后才能检测到。

The lost opportunity here is that everyone involved will most likely waste time after the meeting. The collective agreement during the meeting is often an illusion. As the saying goes: “The devil is in the details.” It’s only when starting to create the mockup for the new screen that the issues will really start to jump out. It’s only when trying to code the abstract requirement that the misinterpretation will happen, and it may only be detected days or weeks later.

更好的做法是回应这种非正统的命题:

A better approach is to reply on this type of unorthodox proposition:

为什么要在这次会议期间一起创建一个具体的例子呢?

Why about creating a concrete example together, right now, during this meeting?

我经常使用类似的策略:

I often use a similar strategy:

我相信我们都同意需要做什么。但为了百分百确定,为了以防万一,我们现在应该花几分钟时间一起起草一个具体的示例。

I believe we’re all in agreement on what needs to be done. But to be 100% sure, just in case, we should take a few minutes to draft a concrete example all together right now.

立即做这种事情听起来像是浪费时间。我有时会听到这样的反对意见:“我们没有时间讨论低级细节”。确实,看到您的同事在大视频屏幕上的 Microsoft PowerPoint 屏幕截图上缓慢地拼贴按钮和面板可能会很痛苦。然而,与此同时,您可以节省更多的决策时间,因为每个人都可以立即确认、调整或发出警报。

It might sound like a waste of time to do this sort of thing right away. “We don’t have time for the low-level details here” is an objection I sometimes hear. And it’s true that it can be painful to observe your colleague slowly working the collage of buttons and panels on a screenshot in Microsoft PowerPoint on the big video screen. However, at the same time, you’re saving much more time in decision making, because everybody is there to confirm, adjust, or raise an alarm—instantly.

因此:每当举办规范研讨会时,请确保每位与会者在会议期间立即就具体示例达成一致。抵制通过离线操作来节省时间的诱惑。承认决策通常是主要瓶颈,而不是起草具体示例。一些生成的示例将成为文档的重要部分。

Therefore: Whenever there’s a workshop on specifications, make sure to have every attendee agree on concrete examples during the meetings—right away during sessions. Resist the temptation to save time by doing it offline. Acknowledge that decision making is often the main bottleneck, not drafting concrete examples. Some of the resulting examples will be important parts of your documentation.

示例是否以文本、数据表、活动挂图草图、投影在大屏幕上的工具中的可视屏幕模型或其他形式表达的场景并不重要。重要的是每个参与的人都理解这些例子,这样他们就能立即注意到其中是否有问题。为此,示例必须具体。不要达成抽象的协议。大家可能都同意“折扣是50%”,但是当价格是1.55美元时,你该如何应对呢?您如何处理舍入?您需要具体的示例来注意到此类问题。

It does not matter if the examples are scenarios expressed in text, data tables, flipchart sketches, visual screen mockups in a tool projected on a big screen, or something else. What matters is that everyone involved understands the examples so that they can immediately notice whether something is wrong in them. For this purpose, it is essential that examples be concrete. Don’t settle on abstract agreement. Everybody may agree that “the discount is 50%” but how do you handle it when the price is $1.55? How do you take care of the rounding? You need concrete examples to notice issues such as this.

在实践中

In Practice

当您第一次建议在会议期间创建具体示例时,您可能会听到许多常见的反对意见。具体的看起来冗长而缓慢,而抽象的则显得简洁而快速。从短期来看确实如此,但从长远来看,在规范的背景下,情况恰恰相反:混凝土更快。

You are likely to hear many common objections the first time you suggest creating concrete examples during the meeting. Concrete seems verbose and slow, whereas abstract seems concise and fast. This is true in the very short term, but in the longer term, in the context of specifications, it’s rather the opposite: Concrete is faster.

事实上,您可能痛苦地意识到这一点,并可能建议离线执行这些示例:“我不想浪费您的时间,所以告诉我如何做,我稍后会自己做。” 但是,请考虑使用以下句子:“抱歉,我启动该工具时您必须等待三分钟,但我们确信我们就解决方案达成了一致。这样我们就可以避免在未来几天和几周内不断发送电子邮件和召开进一步的会议。”

In fact, you may be painfully aware of this and might suggest doing the examples offline: “I don’t want to waste your time, so tell me how to do it, and I’ll do it later on my own.” Instead, however, consider using the following sentence: “Sorry you’ll have to wait for three minutes while I fire the tool, but then we know for sure we’re in agreement on the solution. This way we can avoid a ping-pong of emails and further meetings in the coming days and weeks.”

当涉及到规范时,沟通特别容易出错,请记住以下几点:

When it comes to specifications, where communication is particularly fallible, keep in mind the following:

  • 不要: “我们可以停在那里以节省时间。我一个人继续,然后我们再开会讨论结果。”

  • DON’T: “We can stop there to save time. I’ll go on alone, and then we’ll have another meeting to discuss the results.”

  • 应: “让我们一起尽可能快地走得更远,以便我们尽快知道问题所在以及我们可能存在分歧的地方。”

  • DO: “Let’s go as far and as quickly as we can together so that we know quickly what the troubles are and where we may disagree.”

快速媒体和事先准备

Fast Media and Prior Preparation

它有助于选择快速媒体,例如以下内容,以就具体示例达成共识:

It helps to choose fast media, such as the following, to create consensus on concrete examples:

  • 活动挂图或白板和记号笔,仔细书写以便每个人都能阅读

  • Flipchart or whiteboard and markers, written on carefully so that everybody can read

  • 桌子上有大页纸,还有笔

  • Big pages of paper on the table, with pens

  • 人们交谈,发言人逐字记录,定期向其他与会者重述记录

  • People talking and a spokesperson taking verbatim notes, regularly restating the notes to other attendees

  • 大屏幕上显示的简单文本编辑器

  • A simple text editor shown on a big screen

  • 您熟悉的屏幕模型工具,因此您可以快速使用它

  • A screen mockup tool that you know well so that you’re quick with it

  • 如果您对解决方案感到满意,Microsoft PowerPoint 可以对预先选择的屏幕截图进行拼贴

  • Microsoft PowerPoint to do a collage of preselected screenshots if you’re comfortable with the solution

提前做一些准备并准备一些现成的材料是有帮助的。我曾经让一位同事在每次会议时都会带一个文件夹,里面装满了所有重要屏幕的屏幕截图和应用程序主要工作流程的打印图表,以改善讨论期间的沟通。我使用了类似的方法,但以电子方式进行,保留一个包含屏幕截图和其他内容的默认 PowerPoint,以防万一在规范研讨会期间需要它来回答问题或作为屏幕拼贴的一部分重复使用。

It helps to do some prior preparation and have some ready-made materials. I once had a colleague bring a file folder full of screenshots from all the important screens and printed diagrams of the main workflows of the application to every meeting to improve communication during discussions. I’ve used a similar approach but electronically, keeping a default PowerPoint full of screenshots and other stuff, just in case it is needed during specification workshops to answer questions or to be reused as part of a screen collage.

相同的想法可以推广到规范的其他方面,例如质量属性。在讨论性能、延迟和容错要求时,最好不仅定义期望,而且要加倍努力并共同商定验收标准。验收标准应该成为一项测试,不仅记录而且确保质量属性确实得到满足。

The same ideas can be generalized to other aspects of specifications, such as quality attributes. When discussing performance, latency, and fault tolerance requirements, it would be a good idea to not only define the expectations but to go the extra mile and collectively agree on the acceptance criteria. The acceptance criteria should then become a test that will not only document but also ensure that the quality attributes are actually met.

在一起,现在

Together, Now

“现在一起”的力量意味着在达成协议后要加倍努力,直到所有与会者共同考虑并同意通过具体示例(例如 UI 模型、交互工作流程、影响图、预期业务行为场景)证明的解决方案作为带有准确数字的文本或草图)。

The power of “together, now” suggests going the extra mile after an agreement is made until all attendees together consider and agree to a solution proven by concrete examples (for example, UI mockup, interaction workflow, impact map, scenarios of expected business behavior as text or sketches with accurate numbers on it).

真正产生具体示例的富有成效的规范会议是有价值的。他们依靠面对面的对话进行有效的沟通,并生成高质量的文档作为结果。

Productive specification meetings that really produce concrete examples are valuable. They rely on face-to-face conversations for effective communication, and they produce quality documentation as an outcome.

当然,典型的例子是规格研讨会,其中三个朋友定义了关键场景。关于敏捷软件开发的文献中有很多类似的交互式协作创造具体结果的例子:

The canonical example is, of course, specification workshops where the three amigos define the key scenarios. There are many similar examples of interactive collaborative creation of concrete results in the literature on agile software development:

  • Mob 编程:所有聪明才智聚集在一起,在同一台机器上执行同一个任务

  • Mob programming: All the brilliant minds together, on the same task, on the same machine

  • CRC 卡:一种利用桌上的 CRC 卡进行即时、交互式和协作建模的技术(来自 Ward Cunningham 和 Kent Beck)

  • CRC Cards: A technique for instant, interactive, and collaborative modeling with CRC cards on a table (from Ward Cunningham and Kent Beck)

  • 墙上贴有贴纸的建模:示例为模型风暴(来自 Scott Ambler)3和事件风暴(来自 Alberto Brandolini)4

    3. http://www.drdobbs.com/the-best-kept-secret/184415204

    4. https://www.eventstorming.com/

  • Modeling with stickers on the wall: Examples are model storming (from Scott Ambler)3 and event storming (from Alberto Brandolini)4

    3.http://www.drdobbs.com/the-best-kept-secret/184415204

    4.https://www.eventstorming.com/

  • 代码分析:在与领域专家(来自 Greg Young)的会议期间,直接用编程语言的代码进行建模

  • Code analysis: Modeling directly in code in a programming language during a meeting with the domain expert (from Greg Young)

堆栈溢出文档

Stack Overflow Documentation

我多次听到同事甚至候选人说 Stack Overflow (SO) 是迄今为止获取文档的最佳位置,我的经验也证实了这一点。官方文档页面通常很无聊,而且很少以任务为导向。有趣的是,回答 SO 的人经常不得不使用官方文档页面来构建自己的知识,并进行尝试和错误,有时甚至不得不阅读源代码。

Several times I have heard colleagues or even candidates say that Stack Overflow (SO) is by far the best place to go for documentation, and my experience tends to corroborate this. Official documentation pages are often boring and seldom task oriented. The funny thing is that people answering on SO have often had to use the official documentation pages to build their own knowledge, together with trial and errors or even having to read the source code sometimes.

人们在 SO 上回答问题的速度非常快。这是另一种形式的文档:提出一个问题,世界各地的人们会迅速贡献答案,使文档成为真正活的东西。

People answer questions very quickly on SO. It’s another form of living documentation: Contribute a question, and people all over the world quickly contribute answers, making the documentation a really living thing.

因此:当主题足够流行时,让 SO 在您提供的参考文档之上提供良好的面向任务的文档。让您的团队在 SO 上发布问题,并让他们回答其他人的问题。

Therefore: When the topic is popular enough, let SO provide good task-oriented documentation on top of the reference documentation you provided. Let your teams post questions on SO and let them answer other people’s questions as well.

在 SO 上发布需要您的项目在线发布,通常包含其源代码。它特别要求项目取得成功,有足够的需求来吸引贡献者。

Posting on SO requires your project to be published online, usually with its source code. It especially requires the project to be successful, with enough demand to attract contributors.

或者,您可以将项目保持在内部和闭源状态,并使用等效的本地 Stack Overflow 克隆。5然而,国内的 Stack Overflow 克隆站点可能无法像真正的全球站点那样高效地运行。

Or you can keep your project internal and closed source and use equivalent on-premises Stack Overflow clones.5 However, a domestic Stack Overflow clone will probably not have the scale to work as efficiently as the true worldwide site.

5.StackExchange http://meta.stackexchange.com/questions/2267/stack-exchange-clones

5.StackExchange, http://meta.stackexchange.com/questions/2267/stack-exchange-clones

Stack Overflow 的一个缺点是,一款糟糕的产品会被视为糟糕的产品。但是,当然,除非您使产品变得更好,否则您无法阻止这种情况在网络上发生。您还可以派遣许多员工以积极的方式回答问题,以改善用户体验。

One downside with Stack Overflow is that a product that is awful will be seen as awful. However, you can’t prevent that from happening on the web unless you make the product better, of course. You may also dedicate many employees to answer questions in a positive way to improve the user experience.

价格实惠且有吸引力

Affordable and Attractive

我们可以提供信息,但我们不能让人们关心它。新闻作为解决方案?

We can make information available, but we cannot make people care for it. Journalism as a solution?

——罗梅乌·莫拉

—Romeu Moura

用我的阿罗拉同事罗梅乌·莫拉(Romeu Moura)的话来说,文档应该具有吸引力,就像鲜花具有吸引力一样:为了自我保护。

To paraphrase my Arolla colleague Romeu Moura, documentation should be attractive for the same reason flowers are attractive: for self-preservation.

规格摘要

Specs Digest

我曾经见过一个项目,团队决定将所有设计和规格文档的积累整理成一个更短(大约 10 页长)的“规格摘要”文档。这主要是通过复制和粘贴各种现有文档中最好的部分来完成的,并且在这个过程中更新、修复和补充了明显缺失的部分。该摘要对于团队来说是一份非常有价值的文档。

I once saw a project where the team decided to curate all the accumulation of design and specifications documents into a much shorter (about 10 pages long) “specs digest” document. This was mostly done by copying and pasting the best parts out of various existing documents, and it was updated, fixed, and supplemented with the obviously missing bits in the process. This digest was a highly valued document for the team.

规格摘要被严格组织为几个部分,每个部分通常有半页长,并在目录中概述了清晰的标题。该结构允许安全地跳过任何部分,直接跳转到感兴趣的部分。

A specs digest is strongly organized into sections, each typically a half page long, with clear titles recapped in a table of contents. The structure allows for skipping any section safely to jump directly to the part of interest.

内容主要集中于所有不明显的内容:业务计算(日期、资格、财务和风险计算)、原则和规则。但它也可能描述关键的半技术方面,例如多个相关概念之间的版本控制方案。

The content mostly focuses on everything that is not obvious: business calculations (dates, eligibility, financial and risk calculations), principles, and rules. But it may also describe key semi-technical aspects such as the versioning scheme between multiple related concepts.

请注意,如果您已经拥有基于 Cucumber 等工具中的场景的实时文档,则应将所有这些内容移至功能文件本身或同一文件夹中的 sidecar“序言”Markdown 文件中。

Note that if you already have living documentation based on scenarios in a tool such as Cucumber, you should move all this content into the feature files themselves or into sidecar “preamble” Markdown files in the same folders.

复活节彩蛋和有趣的轶事

Easter Eggs and Fun Anecdotes

享受乐趣是最好的学习方式。您可以通过在文档中隐藏项目、项目发起人和团队成员的轶事来鼓励阅读,从而使任何类型的文档更具吸引力。添加简单的插图。

Having fun is the best way to learn. You can make any kind of documentation more engaging by hiding anecdotes from the project, its sponsors, and team members in the document as a way to encourage reading. Add simple illustrations.

正如 Peter Hilton 在他关于避免文档的演讲中提到的那样:

As Peter Hilton mentions in his talk on documentation avoidance:

使用幽默。没有规定不允许开玩笑。不够严肃的文档可能不是您最大的问题。保持清醒可能是这样。6

Use humor. There’s no rule that says that jokes aren’t allowed. Insufficiently serious documentation is probably not your biggest problem. Staying awake might be.6

6. Peter Hilton,“开发人员避免文档”, https://www.slideshare.net/pirhilton/documentation-avoidance

6.Peter Hilton, “Documentation Avoidance for Developers,” https://www.slideshare.net/pirhilton/documentation-avoidance

宣传新闻

Promoting News

在某处添加知识不足以让受众注意到并使用它。提供推广文档的方法,尤其是当文档发生变化时:

Adding knowledge somewhere is not enough for its audience to notice and use it. Provide ways to promote the documentation, especially when it changes:

  • “最近更改”页面应提供最低限度的信息。

  • A “Recent Changes” page should provide the bare minimum.

  • 更改提要(例如使用 Swagger)可以推送到 Slack。

  • A changes feed (using Swagger, for example) can be pushed to Slack.

  • Slackbot自定义回复可以根据关键词提醒您文档所在位置。

  • Slackbot custom replies can remind you where a document is in response to keywords.

  • 发行说明可能不是必需的。确定您是否真正阅读过它们。

  • Release notes might not be necessary. Determine whether you really read them.

  • 当您真的认真对待知识共享时,请聘请一位真正的专业记者在现场工作。

  • When you’re really serious about knowledge sharing, hire a real professional journalist to work on-site.

非正统媒体

Unorthodox Media

企业界往往缺乏原创性。说到文档,传统媒体仍然是强大的电子邮件、具有无聊强制模板的 Microsoft Office、SharePoint 以及所有因其出色的用户体验而臭名昭著的企业工具。但生活不必如此平淡。使用意想不到的、非正统的媒体进行沟通和记录,让您的团队或部门焕然一新。

The corporate world tends to be unoriginal. When it comes to documentation, the traditional media remain mighty email, Microsoft Office with the boring mandatory templates, SharePoint, and all the various enterprise tools that are notorious for their outstanding user experience. But life does not have to be so dull. Shake up your team or department by using unexpected, unorthodox media for communication and documentation.

以下部分提供了各种想法,可用作灵感来增强您的一般沟通并分享知识和目标。

The following sections provide various ideas to use as inspiration to spice up your communication in general and to share knowledge and objectives.

格言

Maxims

当您当前的举措是提高代码质量时,请提出一句朗朗上口的格言,例如“修复错误?添加一个测试。” 或“对抗遗留代码。编写单元测试。”

When your current initiative is to improve the code quality come up with a catchy maxim, such as “Fix a bug? Add a test.” or “Fight legacy code. Write unit tests.”

不一定要复制和粘贴其他人使用过的格言。创建您自己的、将融入您的文化的产品。要知道一条格言是否会被坚持下去,唯一的方法就是在不同的场合大声说出它,看看它是否能引起共鸣,以及以后是否有人提到它。

Don’t necessarily copy and paste maxims that others have used. Create your own that will stick in your culture. The only way to know if a maxim will stick is to say it out loud on different occasions to see if it resonates and if anyone mentions it later.

笔记

Note

您可能想阅读 Chip Heath 和 Dan Heath 所著的《Made to Stick: Why Some Ideas Survive and Others Die》一书,了解有关此主题的更多信息。

You might want to read the book Made to Stick: Why Some Ideas Survive and Others Die by Chip Heath and Dan Heath to learn more on this topic.

好的格言既有用又有趣。这个来自 Twitter 上的@BeRewt:“如果有疑问,就按照 Erlang 的方式去做。”

Good maxims are useful and amusing at the same time. This one is from @BeRewt on Twitter: “If in doubt, do it the way Erlang does it.”

一旦你有了一条格言,你的工作就是尽可能多地重复它(当然,不要成为垃圾邮件发送者)。

Once you have a maxim, your job is to repeat it as often as possible (without becoming a spammer, of course).

提示

Tip

重复也适用于格言。例如,“可变状态会导致死亡。杀死可变状态!” 有内部重复可以帮助使它更容易记住。

Repetition also works inside a maxim. For example, “Mutable state kills. Kill mutable state!” has internal repetition that can help make it more memorable.

格言必须保持简单,因为复杂的东西无法扩展到更多的受众。你需要传播你的格言,所以准备好用细微差别换取粘性。您只能给出一两个关键信息。确保它们是最重要的消息。以不同的方式处理其他不太重要的消息。

A maxim has to remain trivially simple because complicated stuff does not scale over larger audiences. You need to broadcast your maxims, so be ready to trade nuance for stickiness. You can only give one or two key messages. Make sure they are the most important messages. Take care of the other less important messages in a different way.

笔记

Note

押韵的陈述比不押韵的陈述更可信。这被称为韵律效应伊顿-罗森现象

Statements that rhyme are more believable than those that don’t. This is referred to as the rhyme-as-reason effect or the Eaton-Rosen phenomenon.

海报和国内广告

Posters and Domestic Ads

将您的沟通视为一场营销活动。您可以在内部使用相同的工具。

Think of your communication as a marketing campaign. You can use the same tools but internally.

当你有格言时,你可以把它们变成插图海报。您可以做的第一件事就是从图像搜索开始。例如,考虑一下格言“走得快的唯一方法就是走得好!” 这条格言展示了围绕单词go的内部重复和对称性,这使得它具有粘性。

When you have maxims, you can turn them into illustrated posters. The first thing you can do is start with an image search. For example, consider the maxim “The only way to go fast is to go well!” This maxim exhibits internal repetition and symmetry around the word go, which makes it sticky.

谷歌图像搜索显示了一个现成的模因,在鲍勃叔叔的照片上有这个精确的格言文本,考虑到他喜欢重复这个格言,这并不奇怪(图 15.2 展示了这个模因被重新解释为可爱的怪物

A Google image search turns up a ready-made meme with this exact maxim text over a picture of Uncle Bob, which is not surprising considering that he likes to repeat this maxim (Figure 15.2 gives a taste of this meme reinterpreted as cute monsters).

图中显示了一个卡通人物,上面写着“走得快的唯一方法就是走得好”。

图15.2 Robert C. Martin:走得快的唯一方法就是走得好!

Figure 15.2 Robert C. Martin: The only way to go fast is to go well!

基于模因的海报

Meme-Based Posters

现在考虑一下您还没有格言,但您希望每个人都记得在使用后关闭浴室的门。借助所有可用的免费在线模因生成器,您可以轻松地为此创建励志海报。根据给定的想法,您可以浏览最常见的模因,直到找到最适合您的信息的模因。在这里,我们找到了一位“先生”。T”meme(见图15.3,再次被重新解释为可爱的怪物,因为嘿,它真的很可爱)。(这个例子是我在客户网站上看到的真实例子。浴室门上的海报非常棒。)

Now consider that you don’t have a maxim yet, but you’d like everyone to remember to close the door of the bathroom after use. You can easily create a motivational poster for that, thanks to all the available free online meme generators. From a given idea, you can browse the most common memes until you find one that fits your message best. Here we found a “Mr. T” meme (see Figure 15.3, again re-interpreted as a cute monster, because hey, it’s really cute). (This example is a real one I’ve seen at a customer site. The poster was awesome on the bathroom door.)

图中显示一个卡通人物大喊“你很棒吗?完成后关上门”。

图15.3 你厉害吗?完成后关上门。

Figure 15.3 Are you awesome? Close the door once you’re done.

模因的一个缺点是,如果使用过于频繁,它们往往会变得烦人。

One drawback of memes is that they tend to become annoying when used too frequently.

专家提示

Pro Tip

在您的消息中或消息之间展示一只可爱的小猫。每个人都喜欢可爱的小猫。

Display a cute kitten along with or between your messages. Everybody loves cute kittens.

信息辐射器

Information Radiators

海报不一定要打印并钉在墙壁或窗户上才能可见。有些公司在墙上或电梯里设有电视机,并在上面播放各种幻灯片以供内部交流。这是您张贴海报的好地方。

Posters don’t necessarily have to be printed and pinned on walls or windows to be visible. Some companies have TV sets on the walls or in elevators and show on them a variety of slides for internal communication. This is a nice place for your posters.

缺点是你必须经历一个接受过程,你可能会被拒绝。

The downside is that you have to go through an acceptance process, and you might be rejected.

尽管如此,您仍然可以将海报作为横幅插入到您的构建墙、屏幕保护程序或结对编程拦截器屏幕中。

Still, you can insert your posters as banners into your build walls, screen savers, or pair-programming blocker screens.

幽默和廉价媒体

Humor and Cheap Media

您可能已经看到过图 15.4所示的非常便宜但非常高效的海报。

You might have seen already the very cheap, yet quite efficient, poster shown in Figure 15.4.

一个代表有效故事的人物,显示了一张固定的便条,上面提到“我已经确定了结婚日期,我还没有邀请她出去,软件项目是如何管理的”。

图15.4 一个有效的故事

Figure 15.4 An effective story

讲故事的力量非常强大,即使故事很短。创作这种宝石需要训练或纯粹的运气。幸运的是,您可以出于自己的目的重复使用(窃取或劫持)许多现有的此类宝石。Twitter 是一个非常短且通常很有趣的抄袭故事的重要来源。但请记住,每个人都这样做并不意味着它是合法的。

Storytelling is very powerful, even when the story is this short. It takes training or pure luck to author this kind of gem. Fortunately, you can reuse (steal or hijack) many existing such gems for your own purposes. Twitter is a great source of very short, and often funny, stories to plagiarize. But keep in mind that having everyone doing it does not mean it’s legal.

Maxims 可以足够短以适合主题标签。我们的软件行业也喜欢使用主题标签作为命名新实践的方式;考虑#NoEstimates、#BeyondBudgeting 和#NoProject。

Maxims can be short enough to fit within a hashtag. Our software industry also loves using hashtags as a way to name new practices; consider #NoEstimates, #BeyondBudgeting, and #NoProject.

请注意,主题标签不仅仅适用于 Twitter 和 Facebook。你可以在现实生活中使用它们,甚至可以在口头上使用它们,这听起来很尴尬。

Note that hashtags are not just for Twitter and Facebook. You can use them IRL (in real life), and even verbally, which sounds deliciously awkward.

提示

Tip

以格言命名您的 Wi-Fi 网络。例如,如果您想鼓励环保行为,您可以将您的 Wi-Fi 网络重命名为“ReduceReuseRecycle”。

Name your Wi-Fi network after a maxim. For example, if you’d like to encourage environmentally friendly behavior, you could rename your Wi-Fi network ReduceReuseRecycle.

好东西/赃物

Goodies/Swag

好东西(例如,T 恤、卡片组、备忘单、大型外卖海报、杯子、钢笔、明信片、贴纸、糖果、放松小部件)并不总是绿色的,但有时它们很有用。传统上,礼物被用来重复信息。您可以考虑在商品上印上一句格言,而不是您的品牌。

Goodies (for example, t-shirts, card decks, cheat sheets, large takeaway posters, mugs, pens, postcards, stickers, sweets, relaxation widgets) are not always green, but sometimes they are useful. Goodies have traditionally been used to repeat a message. You might consider printing a maxim, rather than your brand, on goodies.

DDD Europe 会议最近在这方面做得很好,推出了不同格言的不同 T 恤设计,例如

The conference DDD Europe recently did a great job of this with different t-shirt designs with different maxims such as

  • 使隐式显式化(见图15.5

    图中显示了一件印有“MAKE THE IMPLICIT EXPLICIT”的 T 恤。

    图 15.5 制作隐式显式 T 恤

  • MAKE THE IMPLICIT EXPLICIT (see Figure 15.5)

    Figure 15.5 MAKE THE IMPLICIT EXPLICIT t-shirt

  • 扔掉模型

  • THROW AWAY THE MODEL

漫画

Comics

您可以使用漫画作为讲述故事的引人注目的方式,例如梦想更好的软件的沮丧用户的故事。您可以使用漫画来记录和解释新项目的基本原理。

You can use comics as a compelling way to tell a story, such as a story of frustrated users who dream of better software. You might use comics to document and explain the rationale for a new project.

用户完成工作和分享最重要利益的故事也非常适合以易于理解的方式解释和记录业务活动的基本业务利益。

Stories of users doing their job and sharing their most important stakes are also great for explaining—and documenting—in an accessible way the fundamental business stakes of a business activity.

我曾经在企业环境中使用幼稚的漫画来向开发团队解释一个流程。还有一次,我用不那么幼稚的漫画来帮助向一家严肃的大型银行的高级管理层解释治理流程。在这两种情况下,它都奏效并受到赞赏。

I once used childish comics in corporate environments to explain a process for the development team. Another time I used less childish comics to help explain a governance process to senior management in a big, serious bank. In both cases, it worked and was appreciated.

一些在线漫画生成器可以帮助您从角色、设置和效果库中创建基本漫画。它们使任何人都可以创作漫画,即使没有任何绘画技巧。

Several online comics generators can help you create basic comics from libraries of characters, settings, and effects. They make it possible to anyone to create a comic, even without any drawing skill.

信息甲板

Infodecks

信息甲板是用作在屏幕上阅读的文档的幻灯片,而不是投影在观众面前。Infodeck 具有许多优点:

Infodecks are slides used as documents to be read onscreen rather than projected in front of an audience. Infodecks offer many advantages:

  • 您可以使用空间布局来帮助您进行解释。

  • You can use spatial layout to help with your explanation.

  • 他们平易近人,不会使用人们不会阅读的长篇散文。

  • They are approachable and do not use long prose that people won’t read.

  • 将图表作为沟通中的主要元素很容易。

  • It’s easy to include diagrams as primary elements in the communication.

重要的是不要将信息甲板与旨在投影给大量观众的幻灯片混淆。信息甲板应包含很少的文本。它包含的文本应该是很大的字体,并且应该有很多插图。

The important thing is not to confuse infodecks with slide decks that are meant to be projected to a large audience. An infodeck should contain very little text. The text it does include should be in a very big font size, and there should be many illustrations.

Martin Fowler 说:“Infodecks 对我来说是一种有趣的形式,只是因为似乎没有人认真对待它们。……使用大量虚拟页面的丰富多彩、大量图表的方法是一种有效的文档形式,尤其是随着平板电脑变得越来越流行”。7

Martin Fowler says, “Infodecks are an interesting form to me, if only because it seems nobody takes them seriously.…A colorful, diagram-heavy approach that uses lots of virtual pages is an effective form of document, especially as tablets become more prevalent.”7

7. Martin Fowler,ThoughtWorks 博客, http://martinfowler.com/bliki/Infodeck.html

7.Martin Fowler, ThoughtWorks blog, http://martinfowler.com/bliki/Infodeck.html

可视化和动画

Visualizations and Animations

动画和动画可视化比此处列出的其他一些选项更难制作,非常适合解释时间行为。

Animations and animated visualizations, which are a bit more difficult to produce than some of the other options listed here, are perfect for explaining temporal behaviors.

一个很好的例子是 Raft 中分布式共识的美丽可视化,它展示了节点如何在面对各种事件时选举其领导者。8另一个个人最喜欢的想法是通过使用声音和粗略的显示来展示排序算法如何工作的明显疯狂的想法。9

A great example is the beautiful visualization of distributed consensus in Raft, which shows how nodes elect their leader in the face of various events.8 Another personal favorite is the apparently crazy idea of showing how sort algorithms work by using sound along with a crude display.9

8.数据的秘密生活, http://thesecretlivesofdata.com/raft

8.The Secret Lives of Data, http://thesecretlivesofdata.com/raft

9. “6 分钟内 15 种排序算法”, http://m.youtube.com/watch? v=kPRA0W1kECg

9.“15 Sorting Algorithms in 6 Minutes,” http://m.youtube.com/watch?v=kPRA0W1kECg

乐高积木

LEGO Bricks

过去几年,乐高积木在敏捷圈子里变得很流行,现在我们经常在会议期间使用乐高积木,作为规划工具,甚至以 3D 形式物理地表示软件架构。其他化身或构建块系统可以在对话期间用作中介工具。然而,这些结构的问题在于,人们往往会在几天后忘记它们的含义。

LEGO bricks have become popular in agile circles over the past years, and now we often use LEGO bricks during meetings, as a planning tool, or even to represent a software architecture physically in 3D. Other system of avatars or construction blocks can be used as mediation tools during conversations. The problem with these constructions, however, is that people tend to forget what they meant after a few days.

家具

Furniture

甚至你的家具也会讲故事。Fred Georges 在他的一次演讲中解释说,表格表达了初创公司的内部组织:每张表格代表一个项目团队。桌子上没有更多的空间意味着团队已经达到了最大规模。如果桌子未满,如果您愿意,欢迎您加入团队:这是直接提议!

Even your furniture can tell stories. Fred Georges explained in one of his talks that tables express the internal organization of a startup: Each table represents one project team. No more room on the table means the team has reached its maximum size. If a table isn’t full, you’re welcome to join the team if you feel like it: It’s a direct proposition!

此外,你可以从巨大的 iMac 屏幕上看出设计师在哪里,而 Linux 机器更可能暗示开发人员的工作空间。

Furthermore, you can tell from the huge iMac screens where the designers are, whereas Linux machines more likely suggest developers’ workspaces.

3D 打印的东西

3D Printed Stuff

3D 打印模型现在很容易生产。如果您愿意,您可以投影应用程序的特定视图并将其打印在固体材料中。这有助于每个人利用他们的视觉和世界感知能力来通过视觉和触摸来掌握元素。3D 和可移动层可用于表示问题的多个维度,这些维度彼此堆叠且对齐良好。

3D printed models are now easy to produce. You can, if you like, project a particular view of your application and print it in a solid material. This helps everyone use their visual and world-sensing strength to grasp visually and by touching the elements. 3D and removable layers are useful for representing several dimensions of a problem stacked on each other and well aligned.

概括

Summary

有文档并不意味着它有用。牢记一些使文档更具吸引力的原则和想法(例如关注受众、可发现性和有趣因素)可以帮助您优化实时文档计划的有效性。

Having documentation doesn’t mean it’s useful. Keeping in mind some principles and ideas to make documentation more compelling—such as a focus on the audience, discoverability, and the fun factor—can help you optimize the effectiveness of your living documentation initiatives.

指数

Index

数字

Numbers

3D 打印模型,433

3D printed models, 433

4+1型号,358

4+1 model, 358

每个软件架构师都应该知道的 97 件事(高),122

97 Things Every Software Architect Should Know (High), 122

A

A

阿卜杜林·里纳特, 174 , 419420

Abdullin, Rinat, 174, 419420

哈罗德·阿贝尔森,218

Abelson, Harold, 218

丰富的知识,24

abundance of knowledge, 24

滥用生活证件,329330

abuse of living documentation, 329330

知识的可及性, 22 , 24

accessibility of knowledge, 22, 24

过去事件的叙述,33

accounts of past events, 33

准确性

accuracy

确保,3031

ensuring, 3031

人类奉献精神,32

human dedication, 32

具有传播机制的冗余源,32

redundant sources with propagation mechanism, 32

具有调节机制的冗余来源,32

redundant sources with reconciliation mechanism, 32

单一来源出版,31

single source publishing, 31

过去的事件, 33

past events, accounts of, 33

一次性知识,3233

single-use knowledge and, 3233

特别文件,124

ad hoc documents, 124

ADR(建筑决策记录),350354

ADRs (architectural decision records), 350354

阿季奇、戈伊科, 51 , 288

Adzic, Gojko, 51, 288

可供性,309

affordances, 309

聚合体, 207

aggregates, 207

敏捷文档(Rüping),356

Agile Documentation (Rüping), 356

敏捷宣言11,199

Agile Manifesto, 11, 199

敏捷软件开发(Cockburn),270

Agile Software Development (Cockburn), 270

敏捷测试(Crispin 和 Gregory 251、272

Agile Testing (Crispin and Gregory), 251, 272

分析模式(福勒49,263

Analysis Patterns (Fowler), 49, 263

轶事,426

anecdotes, 426

Angular.js 提交指南

Angular.js commit guidelines

变更日志,132133

change logs, 132133

机器可访问的信息,132133

machine-accessible information, 132133

变更范围,具体说明,131132

scope of change, specifying, 131132

变更类型,指定,130131

type of change, specifying, 130131

动画,432433

animation, 432433

注释Doclet,168

AnnotationDoclet, 168

注释

annotations

默认注释,115

annotation by default, 115

AOP(面向方面​​编程)示例,114115

AOP (aspect-oriented programming) example, 114115

在架构文档中,363365

in architecture documentation, 363365

风俗

custom

优点, 109

advantages of, 109

包名称,112

package names, 112

属性, 110

properties, 110

标准注释,扩展,113114

standard annotations, extending, 113114

刻板印象和战术模式,110112

stereotypes and tactical patterns, 110112

嵌入式学习,100102

embedded learning, 100102

示例,99100

examples of, 99100

Google 注释库,103104

Google Annotations Gallery, 103104

JPA(Java 持久性 API),113114

JPA (Java Persistence API), 113114

在遗留应用程序文档中,405、408409、410

in legacy application documentation, 405, 408409, 410

在生活词汇表中,160

in living glossaries, 160

生活服务图示例,200202

living services diagram example, 200202

机器可访问的文档,119120

machine-accessible documentation, 119120

元注释,163

meta-annotations, 163

反对,384

objections to, 384

概述,9799

overview of, 9799

决策背后的理由, 100 , 124

rationales behind decisions, 100, 124

Spring框架113114,201

Spring Framework, 113114, 201

强耦合,163

strong coupling, 163

安塞布尔, 289 , 293

Ansible, 289, 293

AOP(面向方面​​编程),114115

AOP (aspect-oriented programming), 114115

阿帕奇常春藤,291

Apache Ivy, 291

阿帕奇Maven 289,291

Apache Maven, 289, 291

API,防错,308309

APIs, error-proof, 308309

学徒模式( Hoover 和 Oshineye 332、374

Apprenticeship Patterns (Hoover and Oshineye), 332, 374

任意名称,253

arbitrary names, 253

考古学,软件,403404

archeology, software, 403404

架构决策记录 (ADR),350354

architectural decision records (ADRs), 350354

架构文档,370

architecture documentation, 370

建筑法典,359362

architecture codex, 359362

建筑侵蚀,367

architecture erosion, 367

建筑景观,356358

architecture landscape, 356358

变化友好,350

change-friendly, 350

决策日志, 350354 , 383384

decision logs, 350354, 383384

定义, 343344

definition of, 343344

图表和符号,358359

diagrams and notations in, 358359

六边形建筑生活图,179184

hexagonal architecture living diagram, 179184

期刊/博客,355

journals/blogs, 355

模块,355

modules, 355

问题简报,344346

problem briefs, 344346

质量属性,346347

quality attributes, 346347

明确的,346347

explicit, 346347

在生产运行时,369

at runtime in production, 369

作为场景,368369

as scenarios, 368369

目录,370

tables of content, 370

小规模模拟

small-scale simulations

优点, 370372

advantages of, 370372

建筑,374

building, 374

第372

characteristics of, 372

实现的技术,373

techniques to achieve, 373

权益驱动

stake-driven

简短地说,349

brevity of, 349

明确的假设,349

explicit assumptions, 349

概述,347349

overview of, 347349

总结,4546

summary of, 4546

系统隐喻

system metaphors

其他隐喻中的隐喻,376

metaphors in other metaphors, 376

价值, 375

value of, 375

测试驱动架构

test-driven architecture

其他质量属性,370

other quality attributes, 370

概述,367368

overview of, 367368

质量属性作为场景,368369

quality attributes as scenarios, 368369

生产运行时的质量属性,369

quality attributes at runtime in production, 369

目录,370

tables of content, 370

透明架构

transparent architecture

注释,363365

annotations, 363365

建筑现实检查,366367

architecture reality checks, 366367

强制执行准则,365366

enforced guidelines, 365366

概述,362363

overview of, 362363

建筑侵蚀,367

architecture erosion, 367

建筑景观,356358

architecture landscape, 356358

建筑现实检查,366367

architecture reality checks, 366367

建筑单元,365

ArchUnit, 365

安排-行动-断言约定,220221

Arrange-Act-Assert convention, 220221

Ascii 文档,177

AsciiDoc, 177

@Aspect 注释,114

@Aspect annotation, 114

面向方面的编程(AOP),114115

aspect-oriented programming (AOP), 114115

断言J,234

AssertJ, 234

资产捕获,408

asset capture, 408

协会、类型和,227

associations, types and, 227

关联属性,110

Associative property, 110

属性、品质

attributes, quality

明确的,346347

explicit, 346347

其他质量属性,370

other quality attributes, 370

在生产运行时,369

at runtime in production, 369

目录,370

tables of content, 370

目标受众,66

audiences, targeted, 66

可审计性,294

auditability, 294

增强代码。查看 知识扩充

augmented code. See knowledge augmentation

自动化部署,声明式,296297

automated deployment, declarative, 296297

自动化文档

automating documents

挑战,196197

challenges with, 196197

声明式自动化

declarative automation

声明式自动化部署,296297

declarative automated deployment, 296297

声明式配置管理,293296

declarative configuration management, 293296

声明式依赖管理,291293

declarative dependency management, 291293

声明式风格,290

declarative style, 290

机器文档,299

machine documentation, 299

概述,287289

overview of, 287289

脚手架,297299

scaffolding, 297299

信息管理,162164

information curation, 162164

生活图表

living diagrams

业务概览案例研究,184191

business overview case study, 184191

上下文图示例,192196

context diagram example, 192196

对话,以及,171

conversations and, 171

六边形架构示例,179184

hexagonal architecture example, 179184

概述,170171

overview of, 170171

渲染,175178

rendering, 175178

图表比例,174175

scale of diagrams, 174175

存储代码173174

storing code for, 173174

工具,177178

tools for, 177178

可视化指南,178179

visualization guidelines, 178179

包括什么,171174

what to include, 171174

生活词汇表

living glossaries

优点,158159

advantages of, 158159

有界上下文,164165

bounded contexts, 164165

案例研究,165170

case study, 165170

示例,160162

example of, 160162

他们如何工作,160

how they work, 160

概述, 48 , 158

overview of, 48, 158

模式,196197

patterns in, 196197

演示规则,158

presentation rules, 158

作为和解机制,290

as reconciliation mechanism, 290

总结, 43

summary of, 43

避免文档记录。请参阅 文档避免

avoidance of documentation. See documentation avoidance

B

戴夫·巴尔默,327

Balmer, Dave, 327

批处理系统,引入实时文档

batch systems, introducing living documentation in

商业行为,388

business behavior, 388

联系方式,390

contact information, 390

导游, 390

guided tours, 390

旅程测试,390

journey tests, 390

生活图表,389

living diagrams, 389

生活词汇表,389

living glossary, 389

概述,386387

overview of, 386387

自述文件,387

README files, 387

现成的文档,387

ready-made documentation, 387

单一事实来源,388389

single source of truth, 388389

可见的工作原理,388389

visible workings, 388389

BDD(行为驱动开发)

BDD (behavior-driven development)

作为活文件的典型案例,6566

as canonical case of living documentation, 6566

对话,5556

conversations in, 5556

特征文件

feature files

定义, 58

definition of, 58

示例,6365

example of, 6365

词汇表,69

glossaries, 69

意图,58

intent of, 58

非功能性知识的链接,70

links to nonfunctional knowledge, 70

组织,61

organization of, 61

基于属性的测试,6668

property-based testing, 6668

场景, 5859 , 6163

scenarios, 5859, 6163

规格详细信息,5960

specification details, 5960

标签,6061

tags in, 6061

基于属性的测试,6668

property-based testing, 6668

调节,5658

reconciliation in, 5658

冗余,5658

redundancy in, 5658

贝克·肯特, 49 , 218 , 223

Beck, Kent, 49, 218, 223

行为驱动的发展。请参阅 BDD(行为驱动开发)

behavior-driven development. See BDD (behavior-driven development)

参考书目,项目,126127

bibliographies, project, 126127

二进制注释,201

binary annotation, 201

可生物降解的文件,331332

biodegradable documentation, 331332

可生物降解转化,410

biodegradable transformation, 410

破产申请示例,411412

bankruptcy application example, 411412

扼杀者应用示例,411

strangler application example, 411

博客, 124 , 351354

blogs, 124, 351354

添加书签的搜索,257258

bookmarked searches, 257258

埃文·博彻,362

Bottcher, Evan, 362

有界上下文164165,408

bounded contexts, 164165, 408

鲍尔,291

Bower, 291

大脑转储, 89 , 351354

brain dumps, 89, 351354

阿尔贝托·布兰多里尼,275276

Brandolini, Alberto, 275276

易碎玩具,374

breakable toys, 374

简洁,349

brevity, 349

损坏的链接检查器,255256

broken link checkers, 255256

弗雷德·布鲁克斯,317

Brooks, Fred, 317

西蒙·布朗,177、193、358359 _

Brown, Simon, 177, 193, 358359

泡沫背景,404406

bubble context, 404406

虫磁体,288

BugMagnet, 288

构建微服务(纽曼125、308、362

Building Microservices (Newman), 125, 308, 362

业务领域,346

business domains, 346

业务概览生活图

business overview living diagram

背景,184185

background, 184185

变更、住宿190

changes, accommodation of, 190

代码增强,186188

code augmentation, 186188

现有源代码,186

existing source code, 186

生活图表生成,188189

living diagram generation, 188189

活文件模式,以及,191

living documentation patterns and, 191

质量属性,添加,190191

quality attributes, addition of, 190191

业务管道,407

business pipelines, 407

C

C

C#

C#

注释,160

annotations, 160

观光地图,146148

sightseeing map in, 146148

C4 型号, 193 , 358359

C4 model, 193, 358359

骆驼箱,221

CamelCase, 221

CfEngine, 293

CfEngine, 293

变更日志,132133

change logs, 132133

变革管理,398399

change management, 398399

易于更改的文档,350

change-friendly documentation, 350

混沌大猩猩,369

Chaos Gorilla, 369

混沌猴,369

Chaos Monkey, 369

杰里米·查森,207

Chassaing, Jeremie, 207

廉价媒体,430431

cheap media, 430431

廉价的前期文档,36

cheap upfront documentation, 36

厨师, 289 , 293

Chef, 289, 293

克里斯托弗·马丁,29 岁

Christopher, Martin, 29

类路径类,178

ClassPath class, 178

干净的代码。 代码视为文档

clean code. See code as documentation

干净的代码(马丁218,224

Clean Code (Martin), 218, 224

Clojure,121

Clojure, 121

云侦探,201

Cloud Sleuth, 201

阿利斯泰尔·科伯恩,246、268、270、374 _ _ _

Cockburn, Alistair, 246, 268, 270, 374

代码作为文档,218

code as documentation, 218

代码审查,321322

code review, 321322

代码搜索,238239

code searching, 238239

代码驱动图,175

code-driven diagrams, 175

针对框架进行编码,224

coding against a framework, 224

编码约定,221223

coding conventions, 221223

命名约定

naming conventions

任意名称与描述性名称,253

arbitrary versus descriptive names, 253

224的上下文性质

contextual nature of, 224

常青内容,252253

evergreen content, 252253

的重要性,223224

importance of, 223224

可耻的文件,325326

shameful documentation, 325326

文本布局

text layout

安排-行动-断言约定,220221

Arrange-Act-Assert convention, 220221

表格代码布局,219

tabular code layout, 219

UML 视觉符号,219

UML visual notation, 219

代码卡塔,312

CodeKata, 312

手抄本,建筑,359362

codex, architecture, 359362

咖啡机通讯,277280

coffee machine communication, 277280

咖啡脚本,121

CoffeeScript, 121

巧合,编程

coincidence, programming by

深思熟虑的决策,319321

deliberate decision making, 319321

文档作为代码审查,321322

documentation as code review, 321322

概述,317318

overview of, 317318

合作

collaboration

优点, 272273

advantages of, 272273

最佳实践,2122

best practices, 2122

连续记录,276277

continuous documentation, 276277

交叉编程,274

cross programming, 274

事件风暴,275276

Event Storming, 275276

暴民编程,274275

mob programming, 274275

结对编程,273274

pair programming, 273274

“三个朋友”概念,275

“three amigos” concept, 275

卡车系数,277

truck factor, 277

集体所有制,22

collective ownership, 22

集体工作

collective work

优点, 272273

advantages of, 272273

连续记录,276277

continuous documentation, 276277

交叉编程,274

cross programming, 274

事件风暴,275276

Event Storming, 275276

暴民编程,274275

mob programming, 274275

结对编程,273274

pair programming, 273274

“三个朋友”概念,275

“three amigos” concept, 275

卡车系数,277

truck factor, 277

漫画, 432

comics, 432

评论

comments

各种类型的优点228

advantages of types over, 228

可耻的文件,325326

shameful documentation, 325326

标签,102103

tags in, 102103

提交消息

commit messages

优点, 128130

advantages of, 128130

提交指南,129133

commit guidelines, 129133

机器可访问的信息,132133

machine-accessible information, 132133

变更范围,具体说明,131132

scope of change, specifying, 131132

变更类型,指定,130131

type of change, specifying, 130131

公司服务目录,93

company services catalogs, 93

合规要求

compliance requirements

ITIL 案例研究

ITIL case study

概述,397398

overview of, 397398

发布管理,399

release management, 399

变更管理请求,398399

request for change management, 398399

达到/超过,395397

meeting/exceeding, 395397

组合方法,230232

composed method, 230232

具体示例,421423

concrete examples, 421423

快媒体,423

fast media, 423

互动协作创作,424

interactive collaborative creation, 424

事先准备,423

prior preparation, 423

并发版本系统(CVS),108

Concurrent Versions System (CVS), 108

配置管理,声明式,293296

configuration management, declarative, 293296

配置服务器,93

configuration servers, 93

一致性

consistency

设计,310311

designing for, 310311

测试, 7678

tests of, 7678

控制台历史记录,93

console history, 93

合并

consolidation

上下文图示例,194195

context diagram example, 194195

它是如何工作的,8182

how it works, 8182

实施考虑因素,8283

implementation considerations, 8283

需要,8081

need for, 8081

显眼的文件

conspicuous documentation

具体示例,421423

concrete examples, 421423

快媒体,423

fast media, 423

互动协作创作,424

interactive collaborative creation, 424

事先准备,423

prior preparation, 423

复活节彩蛋和有趣的轶事,426

Easter eggs and fun anecdotes, 426

推广, 426

promotion of, 426

规格摘要,425

specs digests, 425

Stack Overflow 文档,424425

Stack Overflow documentation, 424425

未知,关注

unknowns, focus on

灵活的内容,419

flexible content, 419

低保真内容,419

low-fidelity content, 419

要点,描述,417418

salient points, description of, 417418

便于搜索的文档,420421

search-friendly documentation, 420421

按已知受众细分,419

segmenting by known audience, 419

视觉便利,419420

visual facilitation, 419420

非正统媒体,426

unorthodox media, 426

3D 打印模型,433

3D printed models, 433

漫画, 432

comics, 432

国内广告,427429

domestic ads, 427429

家具, 433

furniture, 433

好东西/赃物,431

goodies/swag, 431

幽默和廉价媒体,430431

humor and cheap media, 430431

信息平台,432

infodecks, 432

信息辐射器,429430

information radiators, 429430

乐高积木,433

Lego blocks, 433

格言, 427

maxims, 427

模因,428429

memes, 428429

海报,427429

posters, 427429

可视化和动画,432433

visualizations and animation, 432433

约束行为,307另请参阅 强制指南

constrained behavior, 307. See also enforced guidelines

防错 API,308309

error-proof APIs, 308309

定制服务模板,307308

tailored service templates, 307308

上下文图

context diagrams

增强代码,194195

augmented code, 194195

背景,192193

background, 192193

的优点和局限性,195196

benefits and limitations of, 195196

源代码位置的超链接,193194

hyperlinks to source code locations, 193194

知识巩固,194195

knowledge consolidation, 194195

上下文,有界,164165 , 408

contexts, bounded, 164165, 408

持续交付,1112

continuous delivery, 1112

连续记录,276277

continuous documentation, 276277

持续培训,312313

continuous training, 312313

合同,已公布,7980

contracts, published, 7980

惯例

conventions

遵守106107

adherence to, 106107

编码,221223

coding, 221223

105106的文档

documentation of, 105106

示例,104105

examples of, 104105

107的限制

limitations of, 107

遗留代码中的活文档,105

living documentation in legacy code, 105

对话

conversations

268269的特征

characteristics of, 268269

代码讨论,382383

code discussions, 382383

决策日志,383384

decision logs, 383384

图表和,171

diagrams and, 171

第一次汇报,382

first debriefing, 382

导游,383384

guided tours, 383384

介绍活文件,381382

introducing living documentation, 381382

知识来自,13

knowledge from, 13

障碍,271272

obstacles to, 271272

现成知识,8890

ready-made knowledge in, 8890

三种解释规则,271

Rule of Three Interpretations, 271

威洛定律,270

Wiilo’s laws, 270

@CoreConcept注释,164

@CoreConcept annotation, 164

丽莎·克里斯平, 251 , 272

Crispin, Lisa, 251, 272

克朗,202

cron, 202

交叉编程,274275

cross programming, 274275

黄瓜,57

Cucumber, 57

坎宁安沃德223、230、336 _

Cunningham, Ward, 223, 230, 336

策展。查看 知识整理

curation. See knowledge curation

自定义注释

custom annotations

优点, 109

advantages of, 109

包名称,112

package names, 112

属性, 110

properties, 110

标准注释,扩展,113114

standard annotations, extending, 113114

刻板印象和战术模式,110112

stereotypes and tactical patterns, 110112

CVS(并发版本系统),108

CVS (Concurrent Versions System), 108

D

D

D3.js,177

D3.js, 177

精致的纸,200202

Dapper paper, 200202

数据库、元数据,108109

databases, metadata, 108109

DDD(领域驱动设计),1112

DDD (domain-driven design), 1112

活文件,5053

living documentation and, 5053

概述,4950

overview of, 4950

迪恩·杰夫,278

Dean, Jeff, 278

决策日志, 350354 , 383384

decision logs, 350354, 383384

声明式自动化

declarative automation

声明式自动化部署,296297

declarative automated deployment, 296297

声明式配置管理,293296

declarative configuration management, 293296

声明式依赖管理,291293

declarative dependency management, 291293

声明性指南,303304

declarative guidelines, 303304

声明式风格,290

declarative style, 290

机器文档,299

machine documentation, 299

概述,287289

overview of, 287289

作为和解机制,290

as reconciliation mechanism, 290

脚手架,297299

scaffolding, 297299

当今的装饰艺术(勒·柯布西耶),333

The Decorative Art of Today (Le Corbusier), 333

奉献精神, 32

dedication, 32

深思熟虑的决策,319321

deliberate decision making, 319321

故意发现,319

deliberate discovery, 319

依赖注入 (DI) 容器,212

Dependency Injection (DI) containers, 212

依赖管理,声明式,291293

dependency management, declarative, 291293

依赖管理器,291

dependency managers, 291

设计。查看 生活设计

design. See living design

设计文档,指南对比,305

design documentation, guidelines versus, 305

设计模式(Gamma 等人)102、118

Design Patterns (Gamma et al), 102, 118

开发,文档驱动,326328

development, documentation-driven, 326328

德克西, 122 , 177

Dexy, 122, 177

DI(依赖注入)容器,212

DI (Dependency Injection) containers, 212

诊断工具

diagnostic tools

签名调查,336337

signature surveys, 336337

词云,334336

word clouds, 334336

图表

diagrams

在架构文档中,358359

in architecture documentation, 358359

业务概览案例研究

business overview case study

背景,184185

background, 184185

变更、住宿190

changes, accommodation of, 190

代码增强,186188

code augmentation, 186188

现有源代码,186

existing source code, 186

生活图表生成,188189

living diagram generation, 188189

活文件模式,以及,191

living documentation patterns and, 191

质量属性,添加,190191

quality attributes, addition of, 190191

案例研究,389

case study, 389

上下文图示例

context diagram example

增强代码,194195

augmented code, 194195

背景,192193

background, 192193

的优点和局限性,195196

benefits and limitations of, 195196

源代码位置的超链接,193194

hyperlinks to source code locations, 193194

知识巩固,194195

knowledge consolidation, 194195

对话,以及,171

conversations and, 171

特定领域,205206

domain-specific, 205206

事件溯源场景,209211

event sourcing scenarios, 209211

六边形架构示例,179184

hexagonal architecture example, 179184

概述,170171

overview of, 170171

纯文本

plain-text

优点, 239240

advantages of, 239240

图表作为代码,243244

diagrams as code, 243244

示例,240243

example of, 240243

渲染,175178

rendering, 175178

图表比例,174175

scale of diagrams, 174175

存储代码173174

storing code for, 173174

工具,177178

tools for, 177178

在传统文献中,9

in traditional documentation, 9

可视化指南,178179

visualization guidelines, 178179

包括什么,171174

what to include, 171174

差异,重点,417418

differences, focus on, 417418

摘要,情景,139140

digests, scenario, 139140

乔治·丁威迪, 275 , 287

Dinwiddie, George, 275, 287

分散的事实,巩固。查看 合并

dispersed facts, consolidating. See consolidation

分布式追踪,200202

distributed tracing, 200202

迪塔, 177

ditaa, 177

多科,121

Docco, 121

多克莱特, 168 , 178

Doclet, 168, 178

文件组织,254

document organization, 254

避免文档记录, 4445 , 48

documentation avoidance, 4445, 48

令人惊讶的报告,285

astonishment reports, 285

行为受限,307309

constrained behavior, 307309

持续培训,312313

continuous training, 312313

对话,268272

conversations, 268272

声明式自动化,287299

declarative automation, 287299

按需文档,282

on-demand documentation, 282

设计原则,309311

design principles for, 309311

一致性第一,310311

consistency first, 310311

可替换性第一,310

replaceability first, 310

强制执行指南,300307

enforced guidelines, 300307

想法沉淀,280282

idea sedimentation, 280282

交互式文档,287289

interactive documentation, 287289

即时文档,282285

just-in-time documentation, 282285

机器文档,299

machine documentation, 299

NO文档宣言,267

NODocumentation manifesto, 267

自发讨论,277280

spontaneous discussions, 277280

一次性文档,282

throw-away documentation, 282

前期文档,285287

upfront documentation, 285287

集体工作,272276

working collectively, 272276

文件破产,401402

documentation bankruptcy, 401402

按惯例记录文件。查看 约定

documentation by convention. See conventions

例外记录,306

documentation by exception, 306

文档驱动的开发,326328

documentation-driven development, 326328

领域沉浸,263264

domain immersion, 263264

领域培训,264

domain training, 264

领域愿景声明,260

domain vision statements, 260

领域驱动设计 (DDD),1112

domain-driven design (DDD), 1112

领域驱动设计(埃文斯4,408

Domain-Driven Design (Evans), 4, 408

域名, 商业, 346

domains, business, 346

特定领域图,205206

domain-specific diagrams, 205206

领域特定语言 (DSL), 96 , 232233

domain-specific language (DSL), 96, 232233

特定领域语言(Fowler),235

Domain-Specific Languages (Fowler), 235

特定领域的符号,203205

domain-specific notation, 203205

特定领域测试语言 (DSTL),234235

domain-specific test language (DSTL), 234235

域/子域,408

domains/subdomains, 408

国内广告,427429

domestic ads, 427429

点图,243

DotDiagram, 243

驱动程序(结对编程),273274

drivers (pair programming), 273274

Dropwizard 的指标,308

Dropwizard’s Metrics, 308

DSL(领域特定语言), 96 , 232233

DSL (domain-specific language), 96, 232233

DSTL(特定领域测试语言),234235

DSTL (domain-specific test language), 234235

南希·杜阿尔特, 378 , 394

Duarte, Nancy, 378, 394

双面书,419

duplex books, 419

动态策展

dynamic curation

艺术博物馆策展隐喻,153155

art museum curation metaphor, 153155

知识库,139

corpus of knowledge, 139

编辑策展,137138

editorial curation, 137138

例子,137

examples of, 137

导游

guided tours

创造,148153

creating, 148153

识字编程相比,153

literate programming compared to, 153

概述,144146

overview of, 144146

突出显示的核心,141144

highlighted core, 141144

低维护管理,138

low-maintenance curation, 138

概述,135137

overview of, 135137

情景摘要,139140

scenario digests, 139140

观光地图

sightseeing maps

创造,146148

creating, 146148

概述,144146

overview of, 144146

总结,4243

summary of, 4243

E

复活节彩蛋,426 个

Easter eggs, 426

伊顿-罗森现象,427

Eaton-Rosen phenomenon, 427

日食,229

Eclipse, 229

ECMA 脚本,359

ECMAScript, 359

编辑策展,137138

editorial curation, 137138

效果图,403404

effect maps, 403404

资格,388

eligibility, 388

电子邮件服务器,93

email servers, 93

嵌入式学习, 22 , 100102

embedded learning, 22, 100102

鼓励、强制与303

encouragement, enforcement versus, 303

强制执行的指导方针。另请参阅 受限行为

enforced guidelines. See also constrained behavior

300301的优点

advantages of, 300301

在架构文档中,365366

in architecture documentation, 365366

声明性指南,303304

declarative guidelines, 303304

强制与鼓励,303

enforcement versus encouragement, 303

不断发展的指南,302303

evolving guidelines, 302303

指南与设计文档,305

guidelines versus design documentation, 305

汉克雷斯特示例,306307

Hamcrest example, 306307

遗留规则,413414

legacy rules, 413414

规则示例,301302

rule examples, 301302

工具,304305

tools for, 304305

信任第一的文化,307

trust-first culture, 307

工程

engineering

作为刻意练习,320

as deliberate practice, 320

反向, 343

reverse, 343

侵蚀, 建筑, 367

erosion, architecture, 367

防错 API,308309

error-proof APIs, 308309

埃里克·埃文斯, 4 , 126 , 260 , 405406 , 408

Evans, Eric, 4, 126, 260, 405406, 408

事件溯源,可见测试,207 , 209211

event sourcing, visible tests with, 207, 209211

事件风暴,275276

Event Storming, 275276

常青内容

evergreen content

文件组织,254

document organization, 254

确保稳定性,252253

ensuring stability of, 252253

高级别目标,246247

high-level goals, 246247

247的限制

limitations of, 247

命名约定,252253

naming conventions, 252253

概述, 21 , 245246

overview of, 21, 245246

自述文件案例研究,247251

README file case study, 247251

要求,246

requirements, 246

战略文档,251252

strategy documentation, 251252

不断发展的指南,302303

evolving guidelines, 302303

异常,文档来源,306

exception, documentation by, 306

昂贵的前期文件,36

expensive upfront documentation, 36

知识的利用。参见 知识利用

exploitation of knowledge. See knowledge exploitation

外部演员,194

external actors, 194

外部注释,410

external annotations, 410

外部文档

external documentation

示例,2728

examples of, 2728

与内部文档相比,2526

versus internal documentation, 2526

元数据数据库,108109

metadata databases, 108109

边车文件,108

sidecar files, 108

额外工作,最小化,3739

extra work, minimizing, 3739

极限编程解释(贝克),49

Extreme Programming Explained (Beck), 49

极限编程(XP),375

Extreme Programming (XP), 375

F

F

F#121

F#121

胡萨姆法基赫,274

Fakih, Houssam, 274

快媒体,423

fast media, 423

羽毛,迈克尔,23403

Feathers, Michael, 23, 403

特征文件

feature files

定义, 58

definition of, 58

示例,6365

example of, 6365

词汇表,69

glossaries, 69

意图,58

intent of, 58

非功能性知识的链接,70

links to nonfunctional knowledge, 70

组织,61

organization of, 61

基于属性的测试,6668

property-based testing, 6668

场景, 5859 , 6162

scenarios, 5859, 6162

规格详细信息,5960

specification details, 5960

标签,6061

tags in, 6061

文件

files

决策日志, 350354 , 383384

decision logs, 350354, 383384

特征

feature

定义, 58

definition of, 58

示例,6365

example of, 6365

词汇表,69

glossaries, 69

意图,58

intent of, 58

非功能性知识的链接,70

links to nonfunctional knowledge, 70

组织,61

organization of, 61

基于属性的测试,6668

property-based testing, 6668

场景, 5859 , 6162

scenarios, 5859, 6162

规格详细信息,5960

specification details, 5960

标签,6061

tags in, 6061

清单,291

manifests, 291

边车, 107 , 108

sidecar, 107, 108

灵活的内容,419

flexible content, 419

flottio.fuelcardmonitoring.legacy 包,194195

flottio.fuelcardmonitoring.legacy package, 194195

流畅的界面,233234

fluent interfaces, 233234

流利的 NHibernate,113

Fluent NHibernate, 113

流畅的风格

fluent style

特定领域测试语言 (DSTL),234235

domain-specific test language (DSTL), 234235

流畅的界面,233234

fluent interfaces, 233234

内部特定领域语言 (DSL),232233

internal domain-specific language (DSL), 232233

概述, 232

overview of, 232

何时使用,235

when to use, 235

流畅验证,233

FluentValidation, 233

正式文件,具有挑战性的需求,37

formalized documentation, challenging need for, 37

化石知识,遗留应用程序,402403

fossilized knowledge, legacy applications as, 402403

马丁·福勒,49、230、235、263、296、405 406、419、432 _ _ _ _ _ _ _ _ _ _ _

Fowler, Martin, 49, 230, 235, 263, 296, 405406, 419, 432

分形架构文档,355

fractal architecture documentation, 355

知识碎片化,24

fragmentation of knowledge, 24

框架,编码,224

frameworks, coding against, 224

史蒂夫·弗里曼,180316

Freeman, Steve, 180, 316

FSharp.格式化,121

FSharp.Formatting, 121

燃油卡监控,151

FuelCardMonitoring, 151

燃油卡交易,151

FuelCardTransaction, 151

燃油卡交易报告,151

FuelCardTransactionReport, 151

FuelCardTxListener,150151

FuelCardTxListener, 150151

有趣的活动,3940

fun activities, 3940

有趣的轶事,426

fun anecdotes, 426

家具,作为媒体,433

furniture, as media, 433

G

G

一般知识,2930

general knowledge, 2930

乔治·弗雷德,349

George, Fred, 349

威廉·吉布森,5 岁

Gibson, William, 5

奥利弗·吉尔克,128

Gierke, Oliver, 128

吉尔布,262

Gilb, 262

吉特,92

Git, 92

GitHub 、73、177、308 _ _ _

GitHub, 73, 177, 308

词汇表,69

glossaries, 69

优点,158159

advantages of, 158159

有界上下文,164165

bounded contexts, 164165

案例研究, 165170 , 389

case study, 165170, 389

示例,160162

example of, 160162

他们如何工作,160

how they work, 160

目标

goals

发展中,260261

developing, 260261

影响图,261262

impact mapping, 261262

阿夫拉姆·戈德堡,259

Goldberg, Avram, 259

好东西/赃物,431

goodies/swag, 431

谷歌

Google

注释画廊,103104

Annotations Gallery, 103104

精致的纸,200

Dapper paper, 200

Guava ClassPath 类,178

Guava ClassPath class, 178

格莱德, 289 , 291

Gradle, 289, 291

图形可视化, 4 , 177 , 188 , 210

Graphviz, 4, 177, 188, 210

皮埃尔·保罗·格拉塞,23 岁

Grassé, Pierre-Paul, 23

格雷戈里,珍妮特,251 , 272

Gregory, Janet, 251, 272

以测试为指导,不断发展面向对象的软件(Freeman),180

Growing Object-Oriented Software, Guided by Tests (Freeman), 180

Guava ClassPath 类,178

Guava ClassPath class, 178

导游

guided tours

案例研究, 383384 , 390

case studies, 383384, 390

创造,148153

creating, 148153

识字编程相比,153

literate programming compared to, 153

概述,144146

overview of, 144146

@GuidedTour 注释,149153

@GuidedTour annotation, 149153

指导方针的执行。另请参阅 受限行为

guideline enforcement. See also constrained behavior

300301的优点

advantages of, 300301

声明性指南,303304

declarative guidelines, 303304

强制与鼓励,303

enforcement versus encouragement, 303

不断发展的指南,302303

evolving guidelines, 302303

指南与设计文档,305

guidelines versus design documentation, 305

汉克雷斯特示例,306307

Hamcrest example, 306307

遗留规则,413414

legacy rules, 413414

规则示例,301302

rule examples, 301302

工具,304305

tools for, 304305

信任第一的文化,307

trust-first culture, 307

直觉,91

gut feelings, 91

H

H

汉克雷斯特, 306307

Hamcrest, 306307

主题标签,430

hashtags, 430

哈斯克尔, 121 , 229

Haskell, 121, 229

史蒂夫·霍利,91

Hawley, Steve, 91

亨德里克森,切特,403

Hendrickson, Chet, 403

亨尼,凯夫林,401

Henney, Kevlin, 401

六边形建筑生活图,179184

hexagonal architecture living diagram, 179184

高蒂莫西,122

High, Timothy, 122

高级别目标,246247

high-level goals, 246247

突出显示的核心,141144

highlighted core, 141144

突出显示的结构,408409

highlighted structure, 408409

彼得·希尔顿283284,426

Hilton, Peter, 283284, 426

福尔摩斯,詹姆斯 R.10

Holmes, James R.10

胡格尔,229

Hoogle, 229

戴夫·胡佛, 332 , 374

Hoover, Dave, 332, 374

赫尔,约翰 C.29

Hull, John C.29

幽默和廉价媒体,430431

humor and cheap media, 430431

匈牙利记数法,221222

Hungarian notation, 221222

戴夫·亨特,73 岁

Hunt, Dave, 73

海斯特里克斯,308

Hystrix, 308

I

Icare 故障排除指南,323325

Icare troubleshooting guide, 323325

IDE(集成开发环境)集成文档,238

IDE (integrated development environment) integrated documentation, 238

代码搜索,238239

code searching, 238239

语义,239

semantics, 239

类型层次结构,238

type hierarchy, 238

想法沉淀,280282

idea sedimentation, 280282

幂等性质,110

Idempotent property, 110

按值属性标识,110

Identity by value property, 110

IEEE 1471、358

IEEE 1471, 358

不可变财产,110

Immutable property, 110

影响图,261262

impact mapping, 261262

实施模式(贝克),218

Implementation Patterns (Beck), 218

实施 DDD (弗农),180

Implementing DDD (Vernon), 180

隐性知识,24

implicit knowledge, 24

现场记录,28

in situ documentation, 28

缩进文本,262

indented text, 262

信息平台,432

infodecks, 432

信息墓地,传统文献,10

information graveyard, traditional documentation as, 10

信息辐射器,429430

information radiators, 429430

@Inheritance注解,114

@Inheritance annotation, 114

富有洞察力的文档,22

insightful documentation, 22

仪器仪表, 200

instrumentation, 200

集成开发环境。查看 IDE(集成开发环境)集成文档

integrated development environment. See IDE (integrated development environment) integrated documentation

综合文档, 226227 , 238

integrated documentation, 226227, 238

代码搜索,238239

code searching, 238239

语义,239

semantics, 239

类型层次结构,238

type hierarchy, 238

互动协作创作,424

interactive collaborative creation, 424

交互式文档,287289

interactive documentation, 287289

接口

interfaces

流利,233234

fluent, 233234

反省,214215

Introspectable, 214215

内部文档

internal documentation

定义, 21

definition of, 21

示例,2728

examples of, 2728

与外部文档相比,2526

versus external documentation, 2526

概述,2526

overview of, 2526

偏好, 28

preference for, 28

现场记录,28

in situ documentation, 28

内部特定领域语言 (DSL),232233

internal domain-specific language (DSL), 232233

内部质量,337338

internal quality, 337338

内在知识增强,117119

intrinsic knowledge augmentation, 117119

引入活的文档

introduction of living documentation

批处理系统

batch systems

商业行为,388

business behavior, 388

联系方式,390

contact information, 390

导游, 390

guided tours, 390

旅程测试,390

journey tests, 390

生活图表,389

living diagrams, 389

生活词汇表,389

living glossary, 389

概述,386387

overview of, 386387

自述文件,387

README files, 387

现成的文档,387

ready-made documentation, 387

单一事实来源,388389

single source of truth, 388389

可见的工作原理,388389

visible workings, 388389

常见反对意见,384385

common objections, 384385

合规要求

compliance requirements

ITIL 案例研究,397399

ITIL case study, 397399

达到/超过,395397

meeting/exceeding, 395397

导游,383384

guided tours, 383384

遗留文档,迁移,385386

legacy documentation, migrating, 385386

到管理层

to management

挑战,390391

challenges of, 390391

活文档倡议,392394

living documentation initiatives, 392394

问题简报,391392

problem briefs, 391392

愿景,展示,394395

vision, exhibiting, 394395

边际文档,386

marginal documentation, 386

官方野心,378

official ambition, 378

建议路径

suggested path

变得更大、更引人注目,379380

going big and visible, 379380

轻轻开始,379380

starting gently, 379380

步骤,378379

steps for, 378379

致团队成员

to team members

代码讨论,382383

code discussions, 382383

对话,381382

conversations, 381382

决策日志,383384

decision logs, 383384

第一次汇报,382

first debriefing, 382

秘密实验,377378

undercover experiments, 377378

可自省的界面,214215

Introspectable interface, 214215

内省的运作方式

introspectable workings

反思反思,213214

introspecting with reflection, 213214

不反思地内省,214215

introspecting without reflection, 214215

概述,211212

overview of, 211212

调查墙,264

investigation walls, 264

对稳定知识的投资

investment in stable knowledge

领域沉浸,263264

domain immersion, 263264

领域培训,264

domain training, 264

调查墙,264

investigation walls, 264

“过我的生活”课程,264265

“live-my-life” sessions, 264265

影子用户,265

shadow users, 265

值, 262263 , 265

value of, 262263, 265

ISO/IEC/IEEE 42010, 358

ISO/IEC/IEEE 42010, 358

ITIL 合规案例研究

ITIL compliance case study

概述,397398

overview of, 397398

发布管理,399

release management, 399

变更管理请求,398399

request for change management, 398399

常春藤,291

Ivy, 291

J

J

爪哇

Java

注释,160

annotations, 160

JPA(Java 持久性 API),113114

JPA (Java Persistence API), 113114

命名约定,221

naming conventions, 221

观光地图,146148

sightseeing map in, 146148

Javadoc,74

Javadoc, 74

JAX-RS 注释,114

JAX-RS annotations, 114

罗恩·杰弗里斯,218

Jeffries, Ron, 218

詹金斯,296

Jenkins, 296

吉文,234

JGiven, 234

潮人,298299

JHipster, 298299

JMock,234

JMock, 234

期刊,351354

journals, 351354

旅程测试,390

journey tests, 390

JPA(Java 持久性 API),113114

JPA (Java Persistence API), 113114

即时文档, 35 , 282285

just-in-time documentation, 35, 282285

K

K

利兹·基奥,355

Kheogh, Liz, 355

知识扩充

knowledge augmentation

注释

annotations

默认注释,115

annotation by default, 115

AOP(面向方面​​编程)示例,114115

AOP (aspect-oriented programming) example, 114115

在架构文档中,363365

in architecture documentation, 363365

定制,109114

custom, 109114

嵌入式学习,100102

embedded learning, 100102

示例,99100

examples of, 99100

Google 注释库,103104

Google Annotations Gallery, 103104

JPA(Java 持久性 API),113114

JPA (Java Persistence API), 113114

在遗留应用程序文档中,405、408409、410

in legacy application documentation, 405, 408409, 410

在生活词汇表中,160

in living glossaries, 160

生活服务图示例,200202

living services diagram example, 200202

机器可访问的文档,119120

machine-accessible documentation, 119120

元注释,163

meta-annotations, 163

反对,384

objections to, 384

概述,9799

overview of, 9799

决策背后的理由, 100 , 124

rationales behind decisions, 100, 124

Spring框架113114,201

Spring Framework, 113114, 201

强耦合,163

strong coupling, 163

提交消息

commit messages

优点, 128130

advantages of, 128130

提交指南,129133

commit guidelines, 129133

上下文图示例,194195

context diagram example, 194195

惯例

conventions

遵守106107

adherence to, 106107

105106的文档

documentation of, 105106

示例,104105

examples of, 104105

107的限制

limitations of, 107

遗留代码中的活文档,105

living documentation in legacy code, 105

嵌入式学习,22

embedded learning, 22

外部文档

external documentation

示例,2728

examples of, 2728

与内部文档相比,2526

versus internal documentation, 2526

元数据数据库,108109

metadata databases, 108109

边车文件,108

sidecar files, 108

识字编程, 120122 , 153

literate programming, 120122, 153

机器可访问的文档,119120

machine-accessible documentation, 119120

全模块知识,115116

module-wide knowledge, 115116

内在知识增强,117119

intrinsic knowledge augmentation, 117119

模块类型,116117

module types, 116117

实践中的模块范围增强,117

module-wide augmentation in practice, 117

概述, 95

overview of, 95

编程语言的局限性,9597

programming languages, limitations of, 9597

决策背后的理由,记录

rationales behind decisions, recording

特别文件,124

ad hoc documents, 124

注释, 100 , 124

annotations, 100, 124

博客文章,124

blog posts, 124

启用更改,126

as enabled for change, 126

的重要性,122123

importance of, 122123

要包含的信息,123124

information to include, 123124

项目参考书目,126127

project bibliographies, 126127

目的, 124125 , 126

purpose of, 124125, 126

技能作为预先记录的理由,125

skills as pre-documented rationales, 125

投机、回避、125

speculation, avoidance of, 125

样式声明,127128

style declarations, 127128

知识的稳定性,3839

stability of knowledge, 3839

评论中的结构化标签,102103

structured tags within comments, 102103

总结, 42

summary of, 42

理论构建和传递,1517

theory building and passing, 1517

知识转移, 1718 , 23

transfer of knowledge, 1718, 23

记录什么内容,1819

what to document, 1819

知识积压,286

knowledge backlog, 286

知识管理

knowledge curation

艺术博物馆策展隐喻,153155

art museum curation metaphor, 153155

知识库,139

corpus of knowledge, 139

编辑策展,137138

editorial curation, 137138

例子,137

examples of, 137

导游

guided tours

创造,148153

creating, 148153

识字编程相比,153

literate programming compared to, 153

概述,144146

overview of, 144146

突出显示的核心,141144

highlighted core, 141144

对于生活文件,162164

for living documents, 162164

低维护管理,138

low-maintenance curation, 138

概述,135137

overview of, 135137

情景摘要,139140

scenario digests, 139140

观光地图

sightseeing maps

创造,146148

creating, 146148

概述,144146

overview of, 144146

总结,4243

summary of, 4243

知识利用

knowledge exploitation

权威知识,识别,7186

authoritative knowledge, identifying, 7186

合并

consolidation

上下文图示例,194195

context diagram example, 194195

它是如何工作的,8182

how it works, 8182

实施考虑因素,8283

implementation considerations, 8283

需要,8081

need for, 8081

文档,以及,1213

documentation and, 1213

知识的演变,1314

evolution of knowledge, 1314

直觉,91

gut feelings and, 91

知识的位置,72

location of knowledge, 72

必要性,1415

necessity of, 1415

知识的起源,13

origination of knowledge, 13

概述, 71

overview of, 71

过去的事件, 33

past events, accounts of, 33

现成的文档, 2324 , 8385 , 105 , 387

ready-made documentation, 2324, 8385, 105, 387

和解机制

reconciliation mechanisms

一致性测试,7678

consistency tests, 7678

概述,7576

overview of, 7576

已公布的合同,7980

published contracts, 7980

测试假设的调节,7879

reconciliation on test assumptions, 7879

冗余源,32

redundant sources with, 32

单一来源出版

single-source publishing

示例,7374

examples of, 7374

概述,7273

overview of, 7273

备注, 75

remarks, 75

带有版本号的快照,75

snapshots with version numbers, 75

一次性知识,3233

single-use knowledge, 3233

特定知识与一般知识,2930

specific versus general knowledge, 2930

标准词汇,8587

standard vocabulary, 8587

总结, 41

summary of, 41

工具历史,9293

tools history, 9293

唐纳德·高德纳,120

Knuth, Donald, 120

科佩拉,尤卡,287

Korpela, Jukka, 287

L

L

抒情歌,278279

La Gaité Lyrique, 278279

“面向对象思维教学实验室”(Beck 和 Cunningham),223

“A Laboratory for Teaching Object-Oriented Thinking” (Beck and Cunningham), 223

两英尺定律,278

Law of Two Feet, 278

布局、文字

layout, text

安排-行动-断言约定,220221

Arrange-Act-Assert convention, 220221

表格代码布局,219

tabular code layout, 219

UML 视觉符号,219

UML visual notation, 219

勒·柯布西耶,333

Le Corbusier, 333

精益酒吧,74

Leanpub, 74

遗留应用程序文档,401

legacy application documentation, 401

可生物降解转化,410

biodegradable transformation, 410

破产申请示例,411412

bankruptcy application example, 411412

扼杀者应用示例,411

strangler application example, 411

泡沫背景,404406

bubble context, 404406

公约,105

conventions, 105

文件破产,401402

documentation bankruptcy, 401402

效果图,403404

effect maps, 403404

强制执行的遗留规则,413414

enforced legacy rules, 413414

外部注释,410

external annotations, 410

突出显示的结构,408409

highlighted structure, 408409

作为化石知识的遗留应用程序,402403

legacy applications as fossilized knowledge, 402403

格言,412413

maxims, 412413

迁移到活的文档,385386

migrating into living documentation, 385386

“重写相同的规范”谬误,402

“rewriting the same specs” fallacy, 402

总结,4647

summary of, 4647

叠加结构,406408

superimposed structure, 406408

遗留读取模型,413

legacy read models, 413

乐高积木,433

Lego blocks, 433

莱宁根, 291

Leiningen, 291

图书馆

libraries

断言J,234

AssertJ, 234

点图,243

DotDiagram, 243

吉文,234

JGiven, 234

JMock,234

JMock, 234

流利,234

NFluent, 234

XDoclet,102

XDoclet, 102

许可机制,307

license mechanisms, 307

链接注册表,256

link registries, 256

关联知识

linked knowledge

添加书签的搜索,257258

bookmarked searches, 257258

损坏的链接检查器,255256

broken link checkers, 255256

链接注册表,256

link registries, 256

非功能性知识,70

nonfunctional knowledge, 70

概述,254255

overview of, 254255

源代码位置,193194

source code locations, 193194

标准知识,8687

standard knowledge, 8687

易失性到稳定的依赖关系,255

volatile-to-stable dependencies, 255

LINQ 语法,233

LINQ syntax, 233

聆听文档,315316

listening to documentation, 315316

识字编程, 120122 , 153

literate programming, 120122, 153

“过我的生活”课程,264265

“live-my-life” sessions, 264265

生活建筑文档。查看 架构文档

living architecture documentation. See architecture documentation

生活策展。查看 知识整理

living curation. See knowledge curation

生活设计,315316

living design, 315316

滥用,329330

abuse of, 329330

可生物降解的文件,331332

biodegradable documentation, 331332

避免文档的设计原则,309311

design principles for documentation avoidance, 309311

一致性第一,310311

consistency first, 310311

可替换性第一,310

replaceability first, 310

诊断工具

diagnostic tools

签名调查,336337

signature surveys, 336337

词云,334336

word clouds, 334336

文档驱动的开发,326328

documentation-driven development, 326328

的重要性,338339

importance of, 338339

内部质量,337338

internal quality of, 337338

聆听文档,315316

listening to documentation, 315316

概述,339341

overview of, 339341

拖延, 329330

procrastination by, 329330

通过巧合设计进行编程

programming by coincidence design

深思熟虑的决策,319321

deliberate decision making, 319321

文档作为代码审查,321322

documentation as code review, 321322

概述,317318

overview of, 317318

可耻的文档

shameful documentation

代码文档,325326

code documentation, 325326

Icare 故障排除指南示例,323325

Icare troubleshooting guide example, 323325

概述,322323

overview of, 322323

透明度,332334

transparency, 332334

生活图表。参见 图表

living diagrams. See diagrams

活文档原则

living documentation principles

协作,2122

collaboration, 2122

洞察力, 22

insight, 22

低努力,21

low effort, 21

概述,1920

overview of, 1920

可靠性,2021

reliability, 2021

活文件

living documents

自动生成的挑战,196197

challenges with automated generation of, 196197

创造, 158

creating, 158

定义, 157

definition of, 157

信息管理,162164

information curation for, 162164

生活图表

living diagrams

业务概览案例研究,184191

business overview case study, 184191

上下文图示例,192196

context diagram example, 192196

对话,以及,171

conversations and, 171

六边形架构示例,179184

hexagonal architecture example, 179184

概述,170171

overview of, 170171

渲染,175178

rendering, 175178

图表比例,174175

scale of diagrams, 174175

存储代码173174

storing code for, 173174

工具,177178

tools for, 177178

可视化指南,178179

visualization guidelines, 178179

包括什么,171174

what to include, 171174

生活词汇表

living glossaries

优点,158159

advantages of, 158159

有界上下文,164165

bounded contexts, 164165

案例研究,165170

case study, 165170

示例,160162

example of, 160162

他们如何工作,160

how they work, 160

需要,14

need for, 14

模式,196197

patterns in, 196197

演示规则,158

presentation rules, 158

生活服务图示例,200202

living services diagram example, 200202

知识的位置,72

location of knowledge, 72

物流和供应链管理(克里斯托弗),29

Logistics and Supply Chain Management (Christopher), 29

日志、决策350354、383384

logs, decision, 350354, 383384

伊戈尔·洛维奇,414

Lovich, Igor, 414

低保真内容,419

low-fidelity content, 419

低维护动态管理,138

low-maintenance dynamic curation, 138

阿诺德·洛耶,62 岁

Loyer, Arnauld, 62

中号

M

机器文档,299

machine documentation, 299

机器可访问的文档, 29 , 119120 , 132133

machine-accessible documentation, 29, 119120, 132133

管理,将生活文件出售给

management, selling living documentation to

挑战,390391

challenges of, 390391

活文档倡议,392394

living documentation initiatives, 392394

问题简报,391392

problem briefs, 391392

愿景,展示,394395

vision, exhibiting, 394395

清单,291

manifests, 291

手动转录, 7 , 8

manual transcription, 7, 8

地图

maps

影响图,261262

impact mapping, 261262

观光地图

sightseeing maps

概念,144146

concept of, 144146

创造,146148

creating, 146148

边际文档,386

marginal documentation, 386

边注,121

Marginalia, 121

布莱恩·马里克,202203

Marick, Brian, 202203

Markdown 描述,68

Markdown descriptions, 68

马丁·罗伯特·塞西尔, 218 , 224

Martin, Robert Cecil, 218, 224

匹配器方法,306

Matcher method, 306

克里斯·马茨,327

Matts, Chris, 327

马文, 74 , 289 , 291

Maven, 74, 289, 291

格言

maxims

第427

for conspicuous documentation, 427

在遗留应用程序文档中,412413

in legacy application documentation, 412413

媒体

media

3D 打印模型,433

3D printed models, 433

漫画, 432

comics, 432

国内广告,427429

domestic ads, 427429

家具, 433

furniture, 433

好东西/赃物,431

goodies/swag, 431

幽默和廉价媒体,430431

humor and cheap media, 430431

信息平台,432

infodecks, 432

信息辐射器,429430

information radiators, 429430

乐高积木,433

Lego blocks, 433

格言, 427

maxims, 427

模因,428429

memes, 428429

海报,427429

posters, 427429

可视化和动画,432433

visualizations and animation, 432433

基于模因的海报,428429

meme-based posters, 428429

消息、提交

messages, commit

优点, 128130

advantages of, 128130

提交指南,129133

commit guidelines, 129133

元注释,163

meta-annotations, 163

元数据数据库,108109

metadata databases, 108109

隐喻、系统

metaphors, system

其他隐喻中的隐喻,376

metaphors in other metaphors, 376

价值, 375

value of, 375

方法

methods

匹配器,306

Matcher, 306

监视器(), 151

monitor(), 151

打印方法(), 170

printMethod(), 170

进程(),168169

process(), 168169

toString(), 208 , 212

toString(), 208, 212

指标(Dropwizard),308

Metrics (Dropwizard), 308

迁移遗留应用程序文档,385386

migrating legacy application documentation, 385386

Mindjet MindManager,262

Mindjet MindManager, 262

心灵大师,262

MindMeister, 262

头脑穆普, 262

MindMup, 262

思维节点,262

MindNode, 262

最大限度地减少额外工作,3739

minimizing extra work, 3739

误导性帮助,传统文档,1011

misleading help, traditional documentation as, 1011

全模块知识,115116

module-wide knowledge, 115116

内在知识增强,117119

intrinsic knowledge augmentation, 117119

模块类型,116117

module types, 116117

实践中的模块范围增强,117

module-wide augmentation in practice, 117

Monitor() 方法,151

monitor() method, 151

莫拉,罗梅乌,425

Moura, Romeu, 425

音乐理论生活词汇表,165170

music theory living glossary, 165170

N

纳吉·佐尔坦,241

Nagy, Zoltán, 241

命名约定

naming conventions

注释包,112

annotation packages, 112

任意名称与描述性名称,253

arbitrary versus descriptive names, 253

224的上下文性质

contextual nature of, 224

常青内容,252253

evergreen content, 252253

的重要性,223224

importance of, 223224

餐巾草图,174

napkin sketches, 174

彼得·诺尔, 15 , 374

Naur, Peter, 15, 374

导航器(结对编程),273274

navigators (pair programming), 273274

Neo4j,177

Neo4j, 177

Netflix, 308 , 369

Netflix, 308, 369

新环境,引入活文档

new environments, introduction of living documentation to

批处理系统

batch systems

商业行为,388

business behavior, 388

联系方式,390

contact information, 390

导游, 390

guided tours, 390

旅程测试,390

journey tests, 390

生活图表,389

living diagrams, 389

生活词汇表,389

living glossary, 389

概述,386387

overview of, 386387

自述文件,387

README files, 387

现成的文档,387

ready-made documentation, 387

单一事实来源,388389

single source of truth, 388389

可见的工作原理,388389

visible workings, 388389

常见反对意见,384385

common objections, 384385

合规要求

compliance requirements

ITIL 案例研究,397399

ITIL case study, 397399

达到/超过,395397

meeting/exceeding, 395397

导游,383384

guided tours, 383384

遗留文档,迁移,385386

legacy documentation, migrating, 385386

边际文档,386

marginal documentation, 386

官方野心,378

official ambition, 378

卖给管理层

selling to management

挑战,390391

challenges of, 390391

活文档倡议,392394

living documentation initiatives, 392394

问题简报,391392

problem briefs, 391392

愿景,展示,394395

vision, exhibiting, 394395

建议路径

suggested path

变得更大、更引人注目,380 381

going big and visible, 380381

轻轻开始,379380

starting gently, 379380

步骤,378379

steps for, 378379

致团队成员

to team members

代码讨论,382383

code discussions, 382383

对话,381382

conversations, 381382

决策日志,383384

decision logs, 383384

第一次汇报,382

first debriefing, 382

秘密实验,377378

undercover experiments, 377378

萨姆·纽曼125、308、362 _

Newman, Sam, 125, 308, 362

流利,234

NFluent, 234

NO文档宣言,267

NODocumentation manifesto, 267

非功能性知识,链接到,70

nonfunctional knowledge, linking to, 70

唐·诺曼,309

Norman, Don, 309

诺斯,丹309310,319

North, Dan, 309310, 319

符号

notation

在架构文档中,358359

in architecture documentation, 358359

特定领域,203205

domain-specific, 203205

匈牙利语,221222

Hungarian, 221222

在传统文献中,9

in traditional documentation, 9

NotNull 属性,110

NotNull property, 110

国家公共管理机构,291

npm, 291

努格特, 289 , 291

NuGet, 289, 291

迈克尔·尼加德,351

Nygard, Michael, 351

O

对象设计(Wirfs-Brock),49

Object Design (Wirfs-Brock), 49

对象树

object trees

反思反思,213214

introspecting with reflection, 213214

不反思地内省,214215

introspecting without reflection, 214215

性质,211212

nature of, 211212

对活体文件的反对,384385

objections to living documentation, 384385

面向对象的重组模式(Demeyer),412

Object-Oriented Reengineering Patterns (Demeyer), 412

章鱼部署,296

Octopus Deploy, 296

官方野心,通过引入活文件,378

official ambition, introducing living documentation through, 378

入职、事件风暴以及,275276

onboarding, Event Storming and, 275276

按需文档,282

on-demand documentation, 282

期权、期货和其他衍生品(赫尔),29

Options, Futures, and Other Derivatives (Hull), 29

组织

organization

常青内容, 254

evergreen content, 254

特征文件, 61

of feature files, 61

ORM 引擎, 91

ORM engines, 91

奥希内耶,阿德瓦勒,332 374

Oshineye, Adewale, 332, 374

蒂姆·奥廷格,10224

Ottinger, Tim, 10, 224

P

包管理器, 291

package managers, 291

包名称,112

package names, 112

包信息.java,164165

package-info.java, 164165

包级注释,405

package-level annotations, 405

结对编程,273274

pair programming, 273274

潘多克,177

Pandoc, 177

纸质文档。请参阅 传统文档

paper documentation. See traditional documentation

家长 ID,200

Parent ID, 200

过去的事件, 33

past events, accounts of, 33

企业应用程序架构模式(Fowler),112

Patterns of Enterprise Application Architecture (Fowler), 112

保利,马蒂厄,239

Pauly, Mathieu, 239

PaymentJourneySteps 枚举,152

PaymentJourneySteps enumeration, 152

佩里,迈克尔 L.309

Perry, Michael L.309

佩西奥、卡洛、 317

Pescio, Carlo, 317

托马斯·佩特里克, 121

Petricek, Tomas, 121

吉尔帕特、吉尔斯、 413

Philippart, Gilles, 413

泡菜, 6162 , 66 , 67 , 68

Pickles, 6162, 66, 67, 68

托马斯·皮兰,234

Pierrain, Thomas, 234

纯文本图表,175

plain-text diagrams, 175

优点, 239240

advantages of, 239240

图表作为代码,243244

diagrams as code, 243244

示例,240243

example of, 240243

纯文本文档,66

plain-text documents, 66

植物UML,177

PlantUML, 177

正属性,110

Positive property, 110

海报,427429

posters, 427429

务实的程序员(亨特和托马斯),73

The Pragmatic Programmer (Hunt and Thomas), 73

演示规则,158

presentation rules, 158

汤姆·普雷斯顿·勒纳,258

Preston-Lerner, Tom, 258

基元,226

primitives, 226

活文档的原则

principles of living documentation

协作,2122

collaboration, 2122

洞察力, 22

insight, 22

低努力,21

low effort, 21

概述,1920

overview of, 1920

可靠性,2021

reliability, 2021

printMethod() 方法,170

printMethod() method, 170

问题简报, 344346 , 391392

problem briefs, 344346, 391392

process() 方法,168169

process() method, 168169

拖延症,329330

procrastination, 329330

“编程作为理论构建”(Naur),15

“Programming as Theory Building” (Naur), 15

通过巧合设计进行编程

programming by coincidence design

深思熟虑的决策,319321

deliberate decision making, 319321

文档作为代码审查,321322

documentation as code review, 321322

概述, 317318

overview of, 317318

编程语言的局限性,9597

programming languages, limitations of, 9597

项目参考书目,126127

project bibliographies, 126127

项目结构

project structure

突出显示,408409

highlighted, 408409

叠加,406408

superimposed, 406408

项目跟踪工具,93

project tracking tools, 93

推广文档,426

promoting documentation, 426

“概念证明”,372

“proof of concept”, 372

传播机制,冗余源,32

propagation mechanism, redundant sources with, 32

推进,91

Propel, 91

基于属性的测试,6668

property-based testing, 6668

专有图表,174

proprietary diagrams, 174

纳特·普赖斯, 180 , 316

Pryce, Nat, 180, 316

已公布的合同,7980

published contracts, 7980

已发表的文件

published documents

备注, 75

remarks, 75

单一来源出版,7273

single-source publishing, 7273

带有版本号的快照,75

snapshots with version numbers, 75

拉动系统,283

pull systems, 283

木偶, 289 , 293296

Puppet, 289, 293296

纯财产,110

Pure property, 110

Q

QDox,178

QDox, 178

质量属性,370

quality attributes, 370

明确的,346347

explicit, 346347

在生产运行时,369

at runtime in production, 369

作为场景,368369

as scenarios, 368369

目录,370

tables of content, 370

文件问题,3336

questions of documentation, 3336

R

木筏,433

Raft, 433

乔·雷恩斯伯格,418

Rainsberger, Joe, 418

决策背后的理由,记录

rationales behind decisions, recording

特别文件,124

ad hoc documents, 124

注释, 100 , 124

annotations, 100, 124

博客文章,124

blog posts, 124

启用更改,126

as enabled for change, 126

的重要性,122123

importance of, 122123

要包含的信息,123124

information to include, 123124

项目参考书目,126127

project bibliographies, 126127

目的, 124125 , 126

purpose of, 124125, 126

技能作为预先记录的理由,125

skills as pre-documented rationales, 125

投机、回避、125

speculation, avoidance of, 125

样式声明,127128

style declarations, 127128

自述文件

README files

案例研究,387

case study, 387

常青内容,247251

evergreen content in, 247251

现成的文档, 2324 , 8385 , 105 , 387

ready-made documentation, 2324, 8385, 105, 387

现实检查,建筑,366367

reality checks, architecture, 366367

和解机制

reconciliation mechanisms

自动化,290

automation as, 290

BDD(行为驱动开发),5658

in BDD (behavior-driven development), 5658

一致性测试,7678

consistency tests, 7678

概述,7576

overview of, 7576

已公布的合同,7980

published contracts, 7980

测试假设的调节,7879

reconciliation on test assumptions, 7879

冗余源,32

redundant sources with, 32

知识的可恢复性,24

recoverability of knowledge, 24

冗余

redundancy

BDD(行为驱动开发),5658

in BDD (behavior-driven development), 5658

冗余来源

redundant sources

具有传播机制,32

with propagation mechanism, 32

有调节机制,32

with reconciliation mechanism, 32

保罗·里夫斯,397

Reeves, Paul, 397

可重构的文档

refactorable documentation

案例研究,236237

case study, 236237

代码作为文档,218

code as documentation, 218

针对框架进行编码,224

coding against a framework, 224

编码约定,221223

coding conventions, 221223

命名,223224

naming, 223224

文本布局,219221

text layout, 219221

组合方法,230232

composed method, 230232

流畅的风格

fluent style

特定领域测试语言 (DSTL),234235

domain-specific test language (DSTL), 234235

流畅的界面,233234

fluent interfaces, 233234

内部特定领域语言 (DSL),232233

internal domain-specific language (DSL), 232233

概述, 232

overview of, 232

何时使用,235

when to use, 235

综合文档,238

integrated documentation, 238

代码搜索,238239

code searching, 238239

语义,239

semantics, 239

类型层次结构,238

type hierarchy, 238

概述,217218

overview of, 217218

纯文本图

plain-text diagrams

优点, 239240

advantages of, 239240

图表作为代码,243244

diagrams as code, 243244

示例,240243

example of, 240243

总结, 43

summary of, 43

类型驱动的文档

type-driven documentation

优点, 225226

advantages of, 225226

优于评论,228

advantages over comments, 228

协会, 227

associations, 227

示例,228230

example of, 228230

综合文档,226227

integrated documentation, 226227

弱类型,226

weak typing, 226

防重构知识,21

refactoring-proof knowledge, 21

反射

reflection

反思,213214

introspecting with, 213214

反省无,214215

introspecting without, 214215

注册表,链接,256

registries, link, 256

发布管理,399

release management, 399

可靠性,2021

reliability, 2021

津津乐道,68

Relish, 68

备注, 75

remarks, 75

渲染生活图表,175178

rendering living diagrams, 175178

可替换性,设计,310

replaceability, designing for, 310

报告 DAO,151152

ReportDAO, 151152

要求,稳定性,246

requirements, stability of, 246

共鸣(杜阿尔特), 378 , 394

Resonate (Duarte), 378, 394

RESTful Web 服务,114

RESTful web service, 114

逆向工程,343

reverse engineering, 343

逆向即时文档,284

reverse just-in-time doc, 284

“重写相同的规范”谬误,402

“rewriting the same specs” fallacy, 402

韵理效应,427

rhyme-as-reason effect, 427

克里斯·理查森,308

Richardson, Chris, 308

尼克·罗赞斯基,358359

Rozanski, Nick, 358359

Ruby on Rails,298

Ruby on Rails, 298

红宝石,291

RubyGems, 291

三种解释规则,271

Rule of Three Interpretations, 271

二人规则,283

Rule of Two, 283

规则

rules

遗产,413414

legacy, 413414

演示文稿, 158

presentation, 158

运行时文档

runtime documentation

内省的运作方式

introspectable workings

反思反思,213214

introspecting with reflection, 213214

不反思地内省,214215

introspecting without reflection, 214215

概述,211212

overview of, 211212

生活服务图示例,200202

living services diagram example, 200202

概述, 199

overview of, 199

运行时的质量属性,369

quality attributes at runtime, 369

软件即文档,202203

software as documentation, 202203

总结, 43

summary of, 43

可见的测试

visible tests

特定领域图,205206

domain-specific diagrams, 205206

特定领域的符号,203205

domain-specific notation, 203205

事件溯源场景207、209211

event sourcing scenarios, 207, 209211

示例,208209

example of, 208209

概述, 203

overview of, 203

可见的工作原理,202203

visible workings, 202203

安德烈亚斯·如平,356

Rüping, Andreas, 356

拉塞尔,马特,135

Russell, Matt, 135

S

S

要点,描述,417418

salient points, description of, 417418

盐,293

Salt, 293

科学技术与试验,291

sbt, 291

脚手架,297299

scaffolding, 297299

图表比例,174175

scale of diagrams, 174175

场景

scenarios

钥匙, 66

key, 66

作为活生生的文献,6162

as living documentation, 6162

概述,5859

overview of, 5859

质量属性为,368369

quality attributes as, 368369

情景摘要,139140

scenario digests, 139140

施瓦辛格、阿诺德,213

Schwarzenegger, Arnold, 213

变更范围,具体说明,131132

scope of change, specifying, 131132

搜索

searches

已添加书签,257258

bookmarked, 257258

代码,238239

code, 238239

便于搜索的文档,420421

search-friendly documentation, 420421

思想的沉淀,280282

sedimentation of ideas, 280282

按已知受众细分,419

segmenting by known audience, 419

硒,390

Selenium, 390

自我记录,294

self-documentation, 294

单独的活动,67

separate activities, 67

服务注册处,93

services registry, 93

影子用户,265

shadow users, 265

可耻的文档

shameful documentation

代码文档,325326

code documentation, 325326

Icare 故障排除指南示例,323325

Icare troubleshooting guide example, 323325

概述,322323

overview of, 322323

边车文件,108

sidecar files, 108

观光地图

sightseeing maps

概念,144146

concept of, 144146

创造,146148

creating, 146148

签名调查,336337

signature surveys, 336337

简单测试类,209

SimpleTest class, 209

简单的测试框架,207

Simple.Testing framework, 207

简单,21

simplicity, 21

模拟,小规模

simulations, small-scale

优点, 370372

advantages of, 370372

建筑,374

building, 374

第372

characteristics of, 372

实现的技术,373

techniques to achieve, 373

单一来源出版

single-source publishing

案例研究,388389

case study, 388389

示例,7374

examples of, 7374

概述, 31 , 7273

overview of, 31, 7273

备注, 75

remarks, 75

带有版本号的快照,75

snapshots with version numbers, 75

一次性知识,3233

single-use knowledge, 3233

站点可靠性工程(Beyer 等人),347

Site Reliability Engineering (Beyer et al), 347

技能

skills

作为预先记录的理由,125

as pre-documented rationales, 125

技能矩阵,287

skills matrix, 287

松弛,92

Slack, 92

小规模模拟

small-scale simulations

优点, 370372

advantages of, 370372

建筑,374

building, 374

第372

characteristics of, 372

实现的技术,373

techniques to achieve, 373

快照, 66 , 75

snapshots, 66, 75

SO(堆栈溢出)文档,424425

SO (Stack Overflow) documentation, 424425

软件考古学,403404

software archeology, 403404

软件考古学(Hendrickson),403

Software Archeology (Hendrickson), 403

软件架构文档。查看 架构文档

software architecture documentation. See architecture documentation

软件即文档,202203

software as documentation, 202203

软件系统架构(Rozanski 和 Woods),358359

Software Systems Architecture (Rozanski and Woods), 358359

声纳,304

Sonar, 304

声纳组件,93

sonar components, 93

声纳Qube,365

SonarQube, 365

来源锻造,177

SourceForge, 177

跨度 ID,200

Span ID, 200

规格流,57

SpecFlow, 57

具体知识,2930

specific knowledge, 2930

示例规范(Adzic), 51 , 55

Specification by Example (Adzic), 51, 55

规格详细信息,5960

specification details, 5960

规格摘要,425

specs digests, 425

投机、回避、125

speculation, avoidance of, 125

自发讨论, 91 , 277280

spontaneous discussions, 91, 277280

Spring框架113114,201

Spring Framework, 113114, 201

春罗,298

Spring Roo, 298

知识的稳定性,3839

stability of knowledge, 3839

稳定的文档,48

stable documentation, 48

常青内容,245246

evergreen content, 245246

文件组织,254

document organization, 254

确保稳定性,252253

ensuring stability of, 252253

高级别目标,246247

high-level goals, 246247

247的限制

limitations of, 247

命名约定,252253

naming conventions, 252253

自述文件案例研究,247251

README file case study, 247251

要求,246

requirements, 246

战略文档,251252

strategy documentation, 251252

目标

goals

发展中,260261

developing, 260261

影响图,261262

impact mapping, 261262

对稳定知识的投资,263264

investment in stable knowledge, 263264

领域培训,264

domain training, 264

调查墙,264

investigation walls, 264

“过我的生活”课程,264265

“live-my-life” sessions, 264265

影子用户,265

shadow users, 265

值, 262263 , 265

value of, 262263, 265

关联知识

linked knowledge

添加书签的搜索,257258

bookmarked searches, 257258

损坏的链接检查器,255256

broken link checkers, 255256

链接注册表,256

link registries, 256

非功能性知识,70

nonfunctional knowledge, 70

概述,254255

overview of, 254255

源代码位置,193194

source code locations, 193194

标准知识,8687

standard knowledge, 8687

易失性到稳定的依赖关系,255

volatile-to-stable dependencies, 255

总结, 43

summary of, 43

愿景陈述

vision statements

域名, 260

domain, 260

的重要性, 259

importance of, 259

Stack Overflow 文档,424425

Stack Overflow documentation, 424425

权益驱动架构文档

stake-driven architecture documentation

简短地说,349

brevity of, 349

明确的假设,349

explicit assumptions, 349

概述,347349

overview of, 347349

利益相关者,346

stakeholders, 346

标准知识

standard knowledge

在谈话中,8890

in conversation, 8890

链接到, 8687

linking to, 8687

标准词汇,8587

standard vocabulary, 8587

标准,21

standards, 21

状态设计模式,161

state design pattern, 161

状态转换表,219

state transition tables, 219

斯蒂梅吉, 23

stigmergy, 23

战略文档,251252

strategy documentation, 251252

强耦合,163

strong coupling, 163

结构、项目

structure, project

突出显示,408409

highlighted, 408409

叠加,406408

superimposed, 406408

计算机程序的结构和解释(Abelson、Sussman 和 Sussman),218

Structure and Interpretation of Computer Programs (Abelson, Sussman, and Sussman), 218

评论中的结构化标签,102103

structured tags within comments, 102103

结构师,177

Structurizr, 177

样式声明,127128

style declarations, 127128

叠加结构,406408

superimposed structure, 406408

调查、签名,336337

surveys, signature, 336337

苏斯曼,杰拉德·杰伊,218

Sussman, Gerald Jay, 218

苏斯曼,朱莉,218

Sussman, Julie, 218

杰夫·苏斯纳,355

Sussna, Jeff, 355

赃物, 431

swag, 431

系统隐喻

system metaphors

其他隐喻中的隐喻,376

metaphors in other metaphors, 376

价值, 375

value of, 375

时间

T

桌子

tables

状态转换表,219

state transition tables, 219

目录,370

tables of content, 370

表格代码布局,219

tabular code layout, 219

标签

tags

在评论中,102103

in comments, 102103

在功能文件中,6061

in feature files, 6061

定制服务模板,307308

tailored service templates, 307308

TDD(测试驱动开发),1112

TDD (test-driven development), 1112

团队成员,向他们介绍活文档

team members, introduction of living documentation to

代码讨论,382383

code discussions, 382383

对话,381382

conversations, 381382

决策日志,383384

decision logs, 383384

第一次汇报,382

first debriefing, 382

模板,定制服务,307308

templates, tailored service, 307308

测试驱动架构

test-driven architecture

其他质量属性,370

other quality attributes, 370

概述,367368

overview of, 367368

质量属性作为场景,368369

quality attributes as scenarios, 368369

生产运行时的质量属性,369

quality attributes at runtime in production, 369

目录,370

tables of content, 370

测试驱动开发 (TDD),1112

test-driven development (TDD), 1112

测试

testing

一致性测试,7678

consistency tests, 7678

旅程测试,390

journey tests, 390

以财产为基础,6668

property-based, 6668

测试假设的调节,7879

reconciliation on test assumptions, 7879

可见,203

visible, 203

文本布局

text layout

安排-行动-断言约定,220221

Arrange-Act-Assert convention, 220221

表格代码布局,219

tabular code layout, 219

UML 视觉符号,219

UML visual notation, 219

理论构建和传递,编程,1517

theory building and passing, programming as, 1517

这个参数,96

this parameter, 96

托马斯·大卫,73 岁

Thomas, David, 73

“三个朋友”概念,275

“three amigos” concept, 275

一次性文档,282

throw-away documentation, 282

传统文档的时间成本,8

time cost, of traditional documentation, 8

“现在在一起” 424

“together, now” 424

工具、知识存储在9293中。另请参阅 单独的工具

tools, knowledge stored in, 9293. See also individual tools

toString() 方法, 208 , 212

toString() method, 208, 212

总功能,326

total functions, 326

跟踪 ID,200

Trace ID, 200

跟踪标识符,200

trace identifiers, 200

传统文献,310311

traditional documentation, 310311

行为受限,307

constrained behavior, 307

防错 API,308309

error-proof APIs, 308309

定制服务模板,307308

tailored service templates, 307308

避免文档记录,4445

documentation avoidance, 4445

令人惊讶的报告,285

astonishment reports, 285

行为受限,307309

constrained behavior, 307309

持续培训,312313

continuous training, 312313

对话,268272

conversations, 268272

声明式自动化,287299

declarative automation, 287299

按需文档,282

on-demand documentation, 282

设计原则,309311

design principles for, 309311

强制执行指南,300307

enforced guidelines, 300307

想法沉淀,280282

idea sedimentation, 280282

交互式文档,287289

interactive documentation, 287289

即时文档,282285

just-in-time documentation, 282285

机器文档,299

machine documentation, 299

NO文档宣言,267

NODocumentation manifesto, 267

自发讨论,277280

spontaneous discussions, 277280

一次性文档,282

throw-away documentation, 282

前期文档,285287

upfront documentation, 285287

集体工作,272276

working collectively, 272276

强制指导方针

enforced guidelines

300301的优点

advantages of, 300301

声明性指南,303304

declarative guidelines, 303304

强制与鼓励,303

enforcement versus encouragement, 303

不断发展的指南,302303

evolving guidelines, 302303

指南与设计文档,305

guidelines versus design documentation, 305

汉克雷斯特示例,306307

Hamcrest example, 306307

规则示例,301302

rule examples, 301302

工具,304305

tools for, 304305

信任第一的文化,307

trust-first culture, 307

需要,3637

need for, 3637

问题, 56

problems with, 56

大脑转储,89

brain dump, 89

信息墓地,10

information graveyard, 10

手动转录,7

manual transcription, 7

误导性帮助,1011

misleading help, 1011

符号, 9

notation, 9

精美图表,9

polished diagrams, 9

单独的活动,67

separate activities, 67

时间成本, 8 , 10

time cost, 8, 10

要问的问题,3339

questions to ask, 3339

培训,连续,312313

training, continuous, 312313

@事务注释,113

@Transactional annotation, 113

知识转移、文档记录,1718 , 23

transfer of knowledge, documentation as, 1718, 23

转化,可生物降解,410

transformations, biodegradable, 410

破产申请示例,411412

bankruptcy application example, 411412

扼杀者应用示例,411

strangler application example, 411

透明架构

transparent architecture

注释,363365

annotations, 363365

建筑现实检查,366367

architecture reality checks, 366367

强制执行准则,365366

enforced guidelines, 365366

概述, 332334 , 362363

overview of, 332334, 362363

卡车系数,277

truck factor, 277

信任,缺乏,3435

trust, lack of, 3435

信任第一的文化,307

trust-first culture, 307

推特搜索,137

Twitter searches, 137

类型层次结构,238

type hierarchy, 238

类型驱动的文档

type-driven documentation

优点, 225226

advantages of, 225226

优于评论,228

advantages over comments, 228

协会, 227

associations, 227

示例,228230

example of, 228230

综合文档,226227

integrated documentation, 226227

弱类型,226

weak typing, 226

酸奶黄瓜,66 岁

Tzatziki, 66

扎齐基诺,68

Tzatzikinow, 68

U

U

UML 视觉符号,219

UML visual notation, 219

秘密实验,377378

undercover experiments, 377378

未知,关注

unknowns, focus on

具体示例,421423

concrete examples, 421423

灵活的内容,419

flexible content, 419

低保真内容,419

low-fidelity content, 419

便于搜索的文档,420421

search-friendly documentation, 420421

按已知受众细分,419

segmenting by known audience, 419

视觉便利,419420

visual facilitation, 419420

非正统媒体

unorthodox media

3D 打印模型,433

3D printed models, 433

漫画, 432

comics, 432

国内广告,427429

domestic ads, 427429

家具, 433

furniture, 433

好东西/赃物,431

goodies/swag, 431

幽默和廉价媒体,430431

humor and cheap media, 430431

信息平台,432

infodecks, 432

信息辐射器,429430

information radiators, 429430

乐高积木,433

Lego blocks, 433

格言, 427

maxims, 427

模因,428429

memes, 428429

海报,427429

posters, 427429

可视化和动画,432433

visualizations and animation, 432433

不成文的知识,24

unwritten knowledge, 24

预先决策、深思熟虑的决策与321

upfront decisions, deliberate decisions versus, 321

前期文档

upfront documentation

要包含的信息,285286

information to include, 285286

知识积压,286

knowledge backlog, 286

技能矩阵,287

skills matrix, 287

用户目录邮件列表,92

user directory mailing lists, 92

V

V

核查机制。查看 调节机制

verification mechanisms. See reconciliation mechanisms

版本号,快照,75

version numbers, snapshots with, 75

可见的测试

visible tests

特定领域图,205206

domain-specific diagrams, 205206

特定领域的符号,203205

domain-specific notation, 203205

事件溯源场景207、209211

event sourcing scenarios, 207, 209211

示例,208209

example of, 208209

概述, 203

overview of, 203

可见的工作原理,202203 , 388389另请参阅 可见测试

visible workings, 202203, 388389. See also visible tests

愿景声明,345

vision statements, 345

域名, 260

domain, 260

的重要性, 259

importance of, 259

视觉便利,419420

visual facilitation, 419420

可视化,178179 , 432433另请参阅 图表

visualizations, 178179, 432433. See also diagrams

易失性到稳定的依赖关系,255

volatile-to-stable dependencies, 255

W

行走的骷髅,374

walking skeletons, 374

警告,303

warnings, 303

水冷却器通信,277280

water cooler communication, 277280

弱类型,226

weak typing, 226

杰拉尔德·温伯格,5271

Weinberg, Gerald, 5, 271

韦弗,米克·塞姆,201

Wever, Mick Semb, 201

威洛定律,270

Wiilo’s laws, 270

Wiio,灵眸,270

Wiio, Osmo Antero, 270

丽贝卡·维尔夫斯·布洛克,49359

Wirfs-Brock, Rebecca, 49, 359

路德维希·维特根斯坦,85

Wittgenstein, Ludwig, 85

伍兹,欧因,358359

Woods, Eoin, 358359

词云,334336

word clouds, 334336

集体工作。查看 集体作品

working collectively. See collective work

有效地处理遗留代码(羽毛),403

Working Effectively with Legacy Code (Feathers), 403

一次写入文档,10

write-once documentation, 10

只写文档,10

write-only documentation, 10

XYZ

X-Y-Z

XDoclet 库,102

XDoclet library, 102

XP(极限编程),375

XP (Extreme Programming), 375

年轻的格雷格,207

Young, Greg, 207

曾戈比古玩,262

Zengobi Curio, 262

拉链, 200202 , 390

Zipkin, 200202, 390

伍迪·祖尔, 261 , 274 , 299 , 390

Zuill, Woody, 261, 274, 299, 390

代码片段

Code Snippets

许多标题都包含编程代码或配置示例。要优化这些元素的呈现,请以单栏、横向模式查看电子书,并将字体大小调整为最小设置。除了以可重排文本格式呈现代码和配置之外,我们还提供了模仿印刷书中演示的代码图像;因此,在可回流格式可能会影响代码列表的呈现的情况下,您将看到“单击此处查看代码图像”链接。单击链接可查看打印保真度代码图像。要返回到上一个查看的页面,请单击设备或应用程序上的“后退”按钮。

Many titles include programming code or configuration examples. To optimize the presentation of these elements, view the eBook in single-column, landscape mode and adjust the font size to the smallest setting. In addition to presenting code and configurations in the reflowable text format, we have included images of the code that mimic the presentation found in the print book; therefore, where the reflowable format may compromise the presentation of the code listing, you will see a “Click here to view code image” link. Click the link to view the print-fidelity code image. To return to the previous page viewed, click the Back button on your device or app.

图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像
图像